Developer Portal
Simple, configurable and easy to integrate, these are the tools every engineer needs to get the best out of InOrbit
dev-portal-banner-87dabb5e
Developer Portal

Contents

InOrbit is a RobOps platform aimed at resolving common infrastructure needs and integrating into the broader robotics solution. Here you can find a menu of the different options you can choose from including SDKs, APIs and tools.

Authentication

All API and SDK access is authenticated with the InOrbit system using an API Key which can be obtained and managed through InOrbit Console, in the API Key section. An API Key is always associated with a service user in your account and its access is managed using role-based access control.

Integrating applications

These are interfaces that allow utilizing robot capabilities in other applications. There are various alternatives that are suitable for different types of applications and technologies.

REST APIs

InOrbit REST APIs are best suited for integrating robot functionality in custom applications and business workflows. They include methods to query online and historical robot data, execute remote actions, organize robots in collections, track ongoing and past mission status, inspect audit logs and more.

Embeds

InOrbit's Embeds make it easy to include pre-made robot-specific UI components into any Web Client application. Through the InOrbit Console, you can configure dashboards with the widgets that you need for specific use cases and then using an embed code, make them available as part of your application, giving you total control of the user experience.

Configuration as Code

InOrbit's Configuration as Code makes it easier to define advanced configuration in a friendly format, thus allowing full control of the application with a refined granularity level.

Incident Management

InOrbit provides different ways to integrate robot monitoring capabilities into your overall incident management workflow. Through the Incident Management interface, it’s possible to dispatch robot alerts in real-time, react to them with context-specific actions from external systems and keep track of ongoing incident updates.

Out-of-the-box integrations with Slack, Google Chat, OpsGenie and PagerDuty can be configured through the InOrbit Console and are best suited for standard workflows.

An outgoing Webhook interface and incoming API are available to integrate with additional systems or implement custom integration behaviors. We’ve provided a code sample to make it easier to get started with these APIs.

Integrating robots

InOrbit provides different alternatives to connect your robots. This section will help you select the best option for your use case.

ROS-based agent

If your robot supports ROS, connecting it to InOrbit can be done in less than a minute using our Quickstart installer. From there, you can take control of the customization using a Debian or Docker based installation and/or applying advanced agent settings. You can also use and extend the InOrbit republisher ROS node to be in control of what specific data you want to expose.

Robot SDK

The Robot SDK enables developers to implement bidirectional communication between robots and the cloud using any robotics framework without using ROS. It leverages a stripped down version of the InOrbit agent to manage the communication between your robot and the cloud and provides simplified bindings for popular robot programming languages such as C++.

Edge SDK

The Edge SDK is useful for connecting robots from intermediary applications without requiring the installation of a software agent on individual robots. It provides a set of interfaces using language bindings to connect to the cloud and submit robot data selectively. This SDK is the recommended path for connecting the InOrbit platform through an existing Fleet Manager.

Interoperability standards

If your robot supports the Mass Robotics AMR Interoperability standard, you can connect it to InOrbit without installing any software or building on any SDK. You can configure and get the endpoint URL through the InOrbit Console. VDA-5050 compliant robots can be connected to InOrbit using the VDA-5050 to InOrbit Proxy. You can also find some open source tools and code samples to ease the implementation of interoperability standards on the robot side in our public GitHub organization.

Tools and samples

    Here are some open source code samples and tools to make it easier to integrate and manage robots:

  • The Google Chat Incident Management sample shows how to connect the InOrbit Incident Management webhook to dispatch alerts into a third-party system.

  • This Brickmart Executive Page shows how to use Embeds on a static site. You can see it live here

  • The ros_amr_interop repository brings together resources to help with the adoption of interoperability standards. It includes the massrobotics_amr_sender_py package, which allows you to make a ROS 2 robot compatible with the standard using only configuration.

  • The VDA5050 to InOrbit Proxy is a useful starting point for connecting VDA-5050 compliant devices to InOrbit and it also doubles as a sample code and starting point for using the InOrbit Edge SDK.

  • The ros_inorbit_samples repository provides code samples to make it easier to customize the integration of ROS robots with InOrbit. It includes the inorbit_republisher allowing you to use configuration to manage what data is published to InOrbit, and is available for ROS Melodic and Noetic.

Authentication

InOrbit uses API keys to authenticate API requests, use of Embeds and calls from the Edge SDK. An API key identifies your account’s service user. This key provides security so that no one outside of your account can access your data.

To generate your API Key go to InOrbit Console and open the API Key tab. Here you’ll be able to create a new API Key for your account.

ApiKey

On this tab you are able to create, rename and delete one or more API Keys.

  • To create an API Key, click on the + icon and update the values of the newest row.
  • To delete an API Key, click on the trashcan icon and the key will be removed.
  • To update an API Key, update the values in the editable inputs, the changes will be automatically saved.
  • Click on the eye icon to view the API Key.

With the creation of a new API Key, InOrbit will create a new user named Service user. This user will be linked to the newly created API Key and when the key is used, the actions are logged under Service user. However, you can change this name at any time by updating the "name" input of a user on the API Key tab in the InOrbit Console.

Restricting API requests: To control API access for users in the account, update the role of the user by using the role selector in the API Key tab in the InOrbit Console.

Application Integration

This section describes different ways to integrate InOrbit and third-party applications, including the use of REST APIs and Embeds.

  • InOrbit REST APIs: provide programmatic access to InOrbit's functionality and are best suited for integrating with backend services.

  • Embeds: allow developers to build and embed dashboards containing robot or fleet level information into any applications using no-code.

  • Configuration as Code: allow customers to configure all InOrbit settings using code.

The following sections describe REST APIs, Embeds, and Configuration as Code in detail.

REST APIs

InOrbit offers REST and streaming APIs that enable programmatic access to InOrbit features. Using these APIs you can fetch data related to your robots, trigger actions, integrate with incident management systems and more.

  • Robots
  • Collections & Tags
  • Attributes
  • Localization & Maps
  • Navigation
  • Robot Lock
  • Actions
  • Incidents
  • Mission Tracking
  • Push APIs
  • Advanced Settings
  • Audit Logs

Check the APIs' documentation and specs here

Embeds

InOrbit Embeds allows you to create a dashboard from InOrbit Console and embed it in any application. Leveraging Embeds you can show robot or fleet level information in your applications without having to implement the UI from scratch.

Some example use cases include:

  • Dedicated role based experience on a tablet for a Floor Operator
  • White-labeled end-user portal
  • Unattended team dashboard running on a centralized monitor
  • UI running on your robot’s physical screen to trigger actions

Defining an Embed

Embeds are created by defining a dashboard from the Console and then generating the embedding code for it.

To manage your dashboards go to Robot Data → Dashboards. From there you can create and edit dashboards, including setting visibility rules and modifying sections and widgets. As shown in the following image:

embed-dev-console

Using the gray plus sign you can add new sections. Each section has a name and scope, that can be robot or fleet.

kpi-dash

Using the gray plus sign inside the section you can add widgets from the widgets palette, as shown below:

widget-section

Once you have defined the dashboard, you are ready to embed it into your applications.

Using an Embed in your application

To generate the embedding code, go to InOrbit Console > Robot Data tab > Dashboards section and use the “< >” button on the right, near the top.

using-embed-in-api

Clicking this button shows a code fragment that can be used to embed the dashboard into any HTML page.

code-fragment

You can customize the iframe’s properties to match your application layout. Remember to replace YOUR_APP_KEY_HERE with your InOrbit API Key.

The following image shows how an Embed looks as part of a web application from a fictional company named Brickmart:

embed-looks

Robot Integration

This section describes tools and libraries that can be used to connect robots to InOrbit. The connection can be made from the individual robots or from servers running on the edge.

Depending on the selected solution architecture, you can pick one of the following SDKs:

  • Robot SDK helps developers implement a bidirectional communication channel between robots and InOrbit. It can be used to feed robot data into InOrbit and then implement actions, such as teleoperation and more. The Robot SDK also streamlines integration with ROS and ROS2 by allowing developers to map ROS topics to InOrbit using simple configuration files.
  • Edge SDK simplifies the integration with third-party or custom applications, like fleet managers, or warehouse management systems. It can be used to fed data gathered by edge servers into InOrbit.

The following sections describe both SDKs in detail and provides examples of their usage.

Robot SDK

The Robot SDK enables developers to implement bidirectional communication between robots and the cloud. It leverages the InOrbit agent to implement the core communication mechanism in an efficient and secure way. Particularly the agent takes care of:

  • Adaptive sampling & throttling
  • Connection handling & offline buffering
  • Security / Encryption
  • Scripted actions
  • Video processing

The Robot SDK provides various methods to ease communication between the agent and other software running on your robot, including:

  • Language Bindings: High-level language-specific APIs that communicate with the agent. Currently the only supported programming language is C++11.
  • Helper components: reduce integration friction with ROS-based robots, but providing zero-code integration mappings from ROS topics.

The following diagram shows how the InOrbit agent interfaces with other software running on the robot:

Robot SDK Interfaces

Software on the robot can communicate with the agent using various mechanisms, depending on the chosen software stack.

Integrating with C++

The Robot SDK for C++ is provided as a C++11 header-only library that can be used to communicate with the InOrbit agent from C++ programs. The library can be downloaded from the Robot SDK for C++ GitHub repository. Note that it is designed to work with the non-ROS InOrbit agent (the Setup section explains how to install the agent).

Using the InOrbit Robot SDK C++ Library you’ll be able to publish data from your programs as shown in example in the right column:

#include "robot-sdk-cpp/include/inorbit/inorbit.hpp"
int main() {
 // Create an InOrbitSDK object
 inorbit::InOrbitSDK sdk;

 // Publish custom data
 sdk.sendKeyValue("battery", 0.56);

 // Publish the pose
 double location[] = {5, 4, 0};
 double rotation_quaternion[] = {0, 0, -0.131, 0.991};
 sdk.sendPose("my_reference_frame", location, rotation_quaternion);
 return 0;
}

The example uses the InOrbitSDK class to publish the pose of the robot and it’s battery level.

Non-ROS agent installation

To integrate from C++ you must use the non-ROS InOrbit agent. It can be installed in different ways, including via a Debian package. For simplicity in this documentation a one-line command that downloads and executes the agent installer is used. After installing the agent you’ll be able to start seeing data from your robot, such as CPU usage, when using InOrbit.

As a prerequisite you’ll have to fetch your account key to add a robot to InOrbit. You can get the key from the InOrbit Console in the “Add Robot” page as shown in the next image:

Developer Console Add a Robot

Your account key is the last segment of the URL, A9wXdA5dsD5in this screenshot. Take note of it since you’ll need it for the next step.

Run the following one-line command on your robot to install the agent, remember to replace ${YOUR_ACCOUNT_KEY} with the key you obtained in the previous step:

curl "https://control.inorbit.ai/liftoff/${YOUR_ACCOUNT_KEY}?variant=nonros" | sh

Executing the command will trigger the agent installation process, follow its steps to get the agent running on your robot.

After completing the installation, the agent will automatically start and connect to InOrbit. By default the robot name is its “hostname”.

To be sure that everything is configured correctly you can try the following command to check that the agent is up and running:

curl http://localhost:5000

Executing the above should produce the following output: “InOrbit Agent API: OK”.

If for some reason you don't see your robot connected or reporting data, check the agent logs at ~/.inorbit/local/inorbit_agent.log. You are welcome to contact our Support Team if you need additional help fixing the problem.

You can customize the port and binding address used by the agent by editing ~/.inorbit/local/agent.env.sh and setting the following variables:

  • INORBIT_AGENT_API_PORT: Port used by the agent to listen for connections from the SDK, default values is 5000.
  • INORBIT_AGENT_API_BINDING: Address used by the agent to listen for connections from the SDK, default value is “localhost”.

Installing the C++ library

You can use the following command to include the library in your project as a git-submodule:

git submodule add https://github.com/inorbit-ai/robot-sdk-cpp.git robot-sdk-cpp

This command will create a new directory named robot-sdk-cpp containing the C++ library.

The InOrbit Robot SDK C++ Library is a header-only C++11 library that encapsulates the communication between C++ programs and the InOrbit agent. It is licensed under the MIT license.

Depending on your preferences, you can add the library to your project in a few different ways. The recommended way, if you are already using Git, is to use git-submodules to include the library's repository in your project. Another option is to just grab the files from the repository and copy them into your project.

Note that if you prefer you can download the library from its repository at GitHub.

Publishing data from C++

#include "robot-sdk-cpp/include/inorbit/inorbit.hpp"
int main() {
  // Create an InOrbitSDK object
  inorbit::InOrbitSDK sdk;
  return 0;
}

All the functionality provided by the SDK library is encapsulated in a class called inorbit::InOrbitSDK, so the first steps to communicate with the InOrbit agent is to include the library and then create an object of this class. This is shown in the code block on the right:

This code includes the library, added to the project in the previous section, and creates the sdk object.

By default the sdk object will talk to the agent using TCP, targeting localhost on port 5000. These and other parameters can be changed using more verbose versions of the constructor.

InOrbitSDK(const std::string &agent_host, int agent_port, bool log_errors);

Using this constructor, you can set the agent host, port and if errors should be reported on stderr (default is true).

The sdk object created in the previous step has a sendPose method that you can use to publish poses data from your program. It receives the reference frame, the robot position and its orientation as a quaternion.

Note that InOrbit uses the Right-hand rule to define the axes and rotation ordering, with X representing the forward direction and Y representing a direction towards the left of the robot. The SDK also uses the XYZW notation for quaternions. So, if you are using a different convention for your localization data, you must apply a conversion before calling sendPose.

#include "robot-sdk-cpp/include/inorbit/inorbit.hpp"
int main() {
 // Create an InOrbitSDK object
 inorbit::InOrbitSDK sdk;

 // Publish the pose
 double location[] = {5, 1, 0};
 double rotation_quaternion[] = {0, 0, 0.383, 0.924};
 sdk.sendPose("map", location, rotation_quaternion);
 return 0;
}

The example on the right shows how you can publish a pose to InOrbit:

The code reports the robot pose as being at x = 5, y = 1, z = 0 with a yaw of 45 degrees counterclockwise in the map reference frame.

The sdk object also provides a sendKeyValue method that you can use to publish almost any data value from your program. You can use this method for different data types, including double, integer and strings.

The example on the right shows how you can publish different values to InOrbit:

#include "robot-sdk-cpp/include/inorbit/inorbit.hpp"
int main() {
 // Create an InOrbitSDK object
 inorbit::InOrbitSDK sdk;

 // Publish custom data of different types: string, double, integer
 sdk.sendKeyValue("battery", 0.56);
 sdk.sendKeyValue("pending_tasks", 4);
 sdk.sendKeyValue("serial", "dd2bc73b");

 return 0;
}

This code publishes the battery level, number of pending tasks and the serial number of the robot. The reported data can be queried using APIs or be visualized from InOrbit Control.

Integrating with ROS 1

The InOrbit agent includes native support for ROS, using the publish/subscribe communication mechanism to exchange data with other software running on your robot. The agent automatically detects topics about maps, cameras and more. You can fine tune this configuration from InOrbit Console.

Publishing Custom Data

rostopic pub /inorbit/custom_data/0 std_msgs/String "battery=55"
rostopic pub /inorbit/custom_data/0 std_msgs/String "status=docked"

The SDK custom data mechanism, allows you to publish telemetry data, static/semi-static robot attributes, and events from your robot to InOrbit.

You can publish any string data to /inorbit/custom_data/0 to send it to InOrbit. You can easily send different elements by publishing a key-value pair either by writing a custom node or by leveraging InOrbit's configuration-based republisher node.

The example on the right shows how to publish some values.

Installing and configuring the InOrbit republisher

republishers:
  - topic: "/mission_data"
    msg_type: "my_custom_msgs/MissionData"
    mappings:
    - field: "goal_id.stamp"
      out:
        topic: "/inorbit/custom_data/0"
        key: "mission_status_goal_timestamp"
    - field: "status"
      out:
        topic: "/inorbit/custom_data/0"
        key: "mission_status"
  - topic: "/errors"
    msg_type: "my_custom_msgs/CustomError"
    mappings:
    - field: "error"
      out:
        topic: "/inorbit/custom_data/0"
        key: "error_code"
    - field: "error_message"
      out:
        topic: "/inorbit/custom_data/0"
        key: "error_string"

The node republisher can be installed like any other standard ROS package e.g. for noetic you'd need to install ros-noetic-inorbit-republisher. To illustrate how to use it, let's assume you want to publish data from a complex custom message my_custom_msgs/ComplexCustomMessage.

The InOrbit republisher configuration shown on the right creates the required publishers and subscribers in order to send the data to InOrbit. To see all the possible parameters, please refer to InOrbit's configuration-based republisher node.

The messages that will arrive at InOrbit read:

  • Two key-value messages for each message on the topic /mission_data:

    • mission_status_goal_timestamp=123456789
    • mission_status="ok"
  • Two key-value messages for each message on the topic /errors:

    • error_code=9
    • error_string="something went wrong"

Running the InOrbit republisher

This node can be executed by using the roslaunch tool and a ROS launchfile pointing to the republisher configuration file, as shown in the example.

<launch>
  <node name="inorbit_republisher" pkg="inorbit_republisher" type="republisher.py">
    <param name="config" textfile="/path/to/config.yaml" />
  </node>
</launch>

Integrating with ROS 2

The InOrbit agent includes support for ROS 2, using the publish/subscribe communication mechanism to exchange data with other software running on your robot.

Edge SDK

The Edge SDK can be used to build integrations between third-party or custom applications running on the edge and the InOrbit platform. For example, it can be used to feed data from a fleet manager system into InOrbit, without requiring the installation of software on your individual robots.

Edge-SDK

Here a custom component acts as a proxy between the fleet manager system and InOrbit cloud using the Edge SDK.

Any data fed into InOrbit can then be used and queried from other components, like InOrbit Control or REST APIs. This SDK allows the publishing of data related to multiple robots, including their connection state, odometry, poses and any custom attributes you’d like to track.

The Edge SDK is distributed two ways. As an NPM package , which can be run in Javascript and Typescript projects. Also as a Python package which is used in Python programs.

Installation

For the most up-to-date installation instrutions, please visit our Github repos for the Javascript Edge SDK or Python Edge SDK. The following example is for Javascript.

npm install inorbit@edge-sdk

The Edge SDK can be installed using NPM. This adds the edge-sdk package as a dependency of your project.

Using the SDK

This section explains how to initialize the Edge SDK and publish data to InOrbit. As a prerequisite you should have already installed the npm package and obtained your InOrbit API key.

Initialization

import { InOrbit } from '@inorbit/edge-sdk';

const sdk = new InOrbit({ appKey: "YOUR_INORBIT_APP_KEY" });

The first step is to import the edge-sdk package. Then initialize the SDK by creating an InOrbit object. The SDK can then be used to publish robot related data to InOrbit.

Reporting robot connection status

The sdk object can be used to report the connection status of each robot to InOrbit. In order to do this, you need to know the IDs of your robots and use the connectRobot and disconnectRobot methods.

const robotId = '12345';
await sdk.connectRobot({ robotId, name: 'MyRobot' });

// Perform tasks

await sdk.disconnectRobot(robotId);

The code reports the robot with id 12345 as connected and then as disconnected. Note that when a robot is reported as online, you can also optionally report its name.

The following sections show how data can be published. Note that the robot must be reported as “online” before publishing any data.

Publishing poses

await sdk.publishPose(robotId, {
  ts: new Date().getTime(),
  x: 5,
  y: 3,
  yaw: 0,
  frameId: 'map'
});

To report a time stamped pose to InOrbit, the publishPose method can be used. The code here reports that the robot is currently at the position (5, 3) in the map reference frame.

Publishing odometry

await sdk.publishOdometry(robotId, {
  tsStart: new Date().getTime(),
  ts: new Date().getTime(),
  speed: {
    linear: 10,
    angular: 1
  }
});

The publishOdometry method can be used to report odometry data to InOrbit. This code reports that the robot is moving with a linear speed of 10 m/s and an angular speed of 1 rad/s.

Publishing other attributes

const robotId = '12345';
await sdk.publishCustomDataKV(robotId, {
  battery: 60,
  status: 'Idle'
});

Other custom attribute can also be reported using the publishCustomDataKV method. The code here reports two custom attributes, battery and status.

Final Notes

Using the Edge SDK allows you to feed data from multiple robots into InOrbit. Contact us if you need help to use this SDK, integrate it with your software or generate your robot IDs.

Complete examples about using the Edge SDK to publish data from multiple robots to InOrbit can be found at:

Interoperability

This section details the different solutions provided by InOrbit Platform to work with robot interoperability standards, such as VDA 5050 and MassRobotics AMR interoperability. These standards are useful to orchestrate heterogeneous fleets that mix robots from different vendors.

Companies that use robots can leverage InOrbit Platform to connect standards compliant robots to the cloud without having to set up dedicated infrastructure. It’s also possible to connect robots that use different standards and orchestrate them in a homogeneous way using InOrbit’s UI and/or APIs.

Robot makers can also benefit from InOrbit Platform by using its open source packages to make any robot standard compliant using just configuration files.

The following sections detail the different interoperability options and solutions included in InOrbit Platform for different standards.

MassRobotics AMR Interoperability

The MassRobotics AMR Interoperability Standard allows AMRs from different vendors to publish information about their state, location, velocity, health, etc using a well defined set of messages. Data transmission is done over web sockets.

Robots compliant with this standard can be connected to InOrbit without requiring the installation of the InOrbit agent on them. ROS2 based robots can become compliant with the standard using the massrobotics_amr_sender package.

Connecting robots to InOrbit

Standard compliant robots can be connected to InOrbit without requiring the installation of the InOrbit agent. To do so, you have to configure your robots to point to InOrbit’s receiver.

You can find the receiver URL for your account in the Console, under Admin → Interoperability.

Interoperability

The next step is to configure the receiver URL on your robots to start sending data to InOrbit. The details about this step depend on your specific robot model and software.

Making ROS2 robots compliant with the standard

Using the open source massrobotics_amr_sender ROS package, ROS2 based robots can become compliant with the standard using just configuration files. Check the package documentation for more details and examples.

VDA 5050

The VDA 5050 V 1.1. AGV Communication Interface describes the communication interface for exchanging order and status data between a central master control and automated guided vehicles (AGV).

You can leverage InOrbit Platform infrastructure to provision the required components, including the MQTT broker, and connect your VDA 5050 compliant robots to the cloud.

Contact our support team for more information.

Open-RMF

Open-RMF (Open Robotics Middleware Framework) is a free, open source, modular software system that enables sharing and interoperability between multiple fleets of robots and physical infrastructure, like doors, elevators and building management systems.

InOrbit provides an open source full control fleet adapter for RMF, which enables communication between a fleet of robots controlled by InOrbit and RMF core, allowing centralized coordination and traffic control for a fleet of robots from multiple vendors.

InOrbit's support for RMF makes it possible to use Open-RMF's traffic deconfliction and task-management capabilities while leveraging InOrbit's ability to observe and operate a large variety of robot vendors. Open-RMF and the fleet adapter can be hosted locally or on the cloud.

Check out the RMF InOrbit Examples repository for demos and templates that make it easier to get started with the adapter.

Configuration Management

Basic configuration can be handled through InOrbit Control. For more advanced configuration that also unlocks other benefits, it is recommended to use Configuration as Code. Configuration as Code allows you to customize different InOrbit settings using code. This way you can manage your settings using DevOps best practices and handle complex configurations in an efficient and programmatic way.

Configurations can be coded using JSON and then be managed using InOrbit's command-line interface (CLI) or through REST APIs. The CLI is distributed as a python package and can be installed through pip.

Additionally, using configuration as code offers the following advantages:

  • Version control of changes
  • Workflows: PR, review, approve, traceability
  • CI/CD
  • Reusable and reproducible configurations
  • Rollback of changes
  • Security
  • Auditing
  • Software parametrization

Summary

InOrbit's configuration as code mechanism is built around the concept of configuration objects. These objects and their properties set how InOrbit and your robots must behave. For example a configuration object can define Incident rules.

{
    "kind": "<configuration_kind>",
    "apiVersion": "<api_version>",
    "metadata": {
        "id": "<configuration_id>",
        "scope": "<scope>"
    },
    "spec": {
        "someProperty": "someValue",
        "anotherProperty": "anotherValue",
    }
}

Each configuration object has the following general properties:

  • kind: Type of setting this configuration refers to, for example Incident. The supported kinds are described later in this document.
  • scope: The scope for which this configuration applies.
  • apiVersion: current API version.
  • spec: configuration values.

On the right you can see the schema in JSON format.

As stated above, each configuration object applies to a specific scope, currently the following levels of scope are supported:

  • root: This is a system-wide level with default configurations.
  • account: Applies to the whole account. Can be specified using account or account/<ACCOUNT_ID>.
  • tag: Applies only to robots tagged with a specific tag. Can be specified using tag/<ACCOUNT_ID>/<TAG_ID>.
  • robot: Applies only to a specific robots. Can be specified using robot/<ACCOUNT_ID>/<ROBOT_ID>.

Scopes behave like a hierarchy, each level inherits the values from its parent and can override them or suppress them.

Suppress means that the configuration object must not be inherited from top levels for the specified scope. This is done by setting the spec to null.

Using the InOrbit CLI

This section explains how to install and configure InOrbit's CLI. The CLI allows you to apply, list, and clear configurations. There are two methods for installing the CLI. The prerequisite is having either Python 3 or Docker installed, depending on installation method.

Using the InOrbit CLI with Docker

The InOrbit CLI is distributed in a Docker image which is available on DockerHub. To run the container, follow the commands on the right:

export INORBIT_CLI_API_KEY="account_api_key"
docker run -e INORBIT_CLI_API_KEY=$INORBIT_CLI_API_KEY -v $(pwd):$(pwd) -ti inorbitai/cli

Note: It is possible to mount a volume with all configuration files inside the container to make editing and applying them easier. This example uses the current working directory.

Installing the Package

The other method is to install the official Python package from PyPi.

pip install inorbit-cli

The inorbit-cli package. can be installed using pip by running the command on the right.

Configuring Environment Variables

# Example configuration of environment variables for the CLI
export INORBIT_CLI_API_KEY="account_api_key"
export INORBIT_CLI_VERBOSE="true" #optional

Before using the CLI, you'll need to configure the INORBIT_CLI_API_KEY with your API key. You can get the API key from the Developer Console.

The CLI verbose mode can be enabled by setting the INORBIT_CLI_VERBOSE environment variable to true.

Getting Started with the CLI

By typing the inorbit or inorbit --help command, you will find the CLI's help shown below.

The CLI provides the following main commands:

  • apply: Applies a configuration object.
  • delete: Deletes a configuration object.
  • get: Lists or dumps configuration objects.
  • describe: Get detailed information about configuration objects.

Listing Tags and Robots

# Example output -> Collections
Collection 'Location' has 2 tags
==========  ========
tag name    tag id
==========  ========
HQ          hq
Lab         lab
==========  ========

# Example output -> Robots
name                 id  agent version    online    last seen
------------  ---------  ---------------  --------  --------------------------
Inorbito      foo        4.4.0            False     1970-01-01T00:00:00.000001

# Example output -> Tags
name               description                     collection id    collection name
-----------------  ------------------------------  ---------------  -----------------
hardware-problems  Marked with hardware problems.  tags             Other
HQ                 Headquarters.                   location         Location

To learn more about your entities, just run the inorbit describe <entity> command. You can query the following entities:

  • collections: Describe all collections and detailed collection data.
  • robots: Describe all robots and detailed robot data.
  • tags: Describe all tags and detailed tag data.

Applying a Configuration Object

The apply command can be used to create or update configuration objects. To apply a configuration object follow these steps:

  1. Create a JSON file describing the configuration object. (The reference schemas for the different kinds are described in following sections)

  2. Run inorbit apply -f config_file.json. If everything goes well, you'll a message like Configuration <config/file/path> applied successfully.

  3. You can use inorbit get config --scope "<scope>" --kind "<kind>" "<object_id>" --dump to check that the configuration object was correctly saved in InOrbit.

Refer to the advanced configuration section for a detailed documentation on how to use scopes.

The kind includes statuses, incident definition, etc. Find their reference in the following sections.

Deleting a Configuration Object

To delete a configuration object, run inorbit delete config --kind "<kind>" "<object_id>" --scope "<scope>".

You will receive an ok response if the configuration object was successfully deleted.

To make sure that the configuration object was deleted, run inorbit get config --scope "<scope>" --kind "<kind>" "<object_id>" and check that there are no results.

Listing Configuration Objects

Example listing of all config objects of a given kind

$ inorbit get config --scope "*" --kind "Kind"
id                kind                label       scope                      suppressed
----------------  ------------------  -------     -------------------------  ------------
foo               Kind                Battery     account/bar                False
bar               Kind                Other Label tag/baz/qux                False
baz               Kind                            robot/qux/quux             True

Example: Detailed JSON dump of all IncidentDefinition objects applying to the entire account

$ inorbit get config --scope "tag/foo/bar" --kind "IncidentDefinition" --dump
[
  {
    "metadata": {
      "scope": "tag/foo/bar",
      "id": "bar"
    },
    "apiVersion": "v0.1",
    "spec": {

    },
    "kind": "IncidentDefinition"
  }
]

The CLI can query and list any kind of configuration objects using: inorbit get config --scope "<scope>" --kind "<kind>".

The --dump option can also be used to dump the contents (in JSON form) of the objects instead of just listing them.

Note that by default the operation lists any configuration object defined for the account, but system defaults are hidden. They can be included adding the --include-system-defaults option.

Advanced: Configuration Scopes

The Config API supports different kinds of objects to configure: for example Actions, Statuses or Incidents.

Normally, these configurations are applied account-wide, meaning that every robot in the account's fleet will make use of the configured objects. This is the case for all configurations applied in the Settings section of the app (unless otherwise stated).

As the robot fleets grow, it is common to require different configurations for several reasons, including for example:

  • Distinguishing production robots operating in the field versus lab robots.
  • Different robot models in a heterogeneous fleet.
  • Managing specific settings that apply only to certain robot versions.
  • Individual robots may be configured with specific settings while debugging one problem.

To address these needs, InOrbit Config API allows user to apply settings to subsets of the robot fleet, using a hierarchy based on scopes.

A scope defines the extent of the account to which a given configuration object applies. It is usually composed of two parts: a type and the identifier. The valid values are of the form:

  • "account", for the entire account associated to an app key; or "account/<accountId>" to explicitly refer to a given account.
  • "tag/<accountId>/<tagId>" for the set of robots containing a given tag <tagId>.
  • "robot/<accountId>/<robotId>" to apply a configuration on an individual robot.

Note that configurations at different scopes can contradict each other -- or rather, to re-define or apply different configurations. For example, consider a robot with id C3P0 with two tags: interpreter and tatooine. This robot will get applied any configuration from its account, as well as configurations for its tags (tag/interpreter and tag/tatooine) and configurations at robot level robot/C3P0.

If a configuration object exists in more than one scope, the more specific scope would override the settings from more generic scopes. For example, robot has precedence over any other configuration (tag and account), and tag has precedence over account.

In this way, one can start with global configurations and also define specific settings for robot types, software versions, field locations, assigned customers, etc -- whichever form the account was configured.

When this configuration complexity appears, it is often the case that using Configuration as Code best practices become necessary too: for example applying configurations programmatically as opposed to manually applying settings, and using version-control to reliably maintain and audit the changes.

Configuring: Data Sources

DataSourceDefinition configuration full schema

{
  "metadata": {
    "scope": "account",
    "id": "<dataSourceId>"
  },
  "apiVersion": "v0.1",
  "kind": "DataSourceDefinition",
  "spec": {
     "label": "",
     "unit": "",
     "scale": 1,
     "type": "",
     "timeline": {},
     "source": {
        "keyValue": {
          "key": ""
        },
        "rosDiagnostics": {
          "namespace": "",
          "key": ""
        },
        "networkUsage": {
          "interface": ""
        },
        "diskUsage": {
          "partition": ""
        },
        "textFile": {
          "path": ""
        },
        "imageFile": {
          "path": ""
        },
        "derived": {
          "transform": "",
          "filter": ""
        }
      }
  }
}

DataSourceDefinition example

{
  "metadata": {
    "scope": "account",
    "id": "<dataSourceId>"
  },
  "apiVersion": "v0.1",
  "kind": "DataSourceDefinition",
  "spec": {
    "label": "",
    "unit": "",
    "type": "",
    "timeline": {},
    "source": {
      "rosDiagnostics": {
        "namespace": "/Navigation/Docking",
        "key": "Docking Status"
      }
    }
  }
}

Configuring DataSourcesDefinition objects define which data from a robot is retrieved and turned into robot attributes for visualization, defining statuses and incidents for monitoring and alerting, etc. Refer to the documentation for the basics on Data Sources.

All Data Sources have a label, and optionally a unit (for example "m/s" for a data source representing a velocity), which are used when displaying data in the app and alerts. Optionally, one can define a type to force interpreting the value as an "array" or a "json"-encoded object. When not defined, the type is "guessed" from the value received from the robot (usually a string, depending on the source): a number if there are digits, a boolean for the strings "true" and "false", or left unparsed as a string otherwise.

Values can be scaled using scale field, which multiplies the value reported by the robot before storing it. This is specially useful for percentage data sources (with unit: "%") which need to be in the range [0..1] to be interpreted and displayed properly. If the robot sends these values in the range [0..100], then using scale: 0.01 will scale them so they get displayed as percentages.

What defines how the data is captured is the source field. It can contain only one of several possible keys, for each of the accepted sources:

  • keyValue: { key: "a key" }: Receives data published in the agent as a key-value pair in the form "a key=some value". The most common mechanism is through a ROS message in topic /inorbit/custom_data/0; in this way, multiple data sources can be transmitted through the same topic. Alternatively, key-value pairs can also be received through SDK in non-ROS environments.
  • rosDiagnostics: { namespace: "a diagnostics path", key: "a key" }: Captures data published in ROS diagnostics messages, for one specific namespace or path, and one key.
  • networkUsage: { interface: "a network interface" }: Captures the network bandwidth usage of a specific network interface in the computer the agent is running on. Note that by default InOrbit agents already capture an overall network usage and the specific bandwidth used by InOrbit, but in some cases (for example if the overall usage includes intra-cpu traffic such as containers) the usage through a specific network card (wifi, 4G) may be of interest.
  • diskUsage: { partition: "device path" }: Represents the total usage (as a fraction: from 0 to 1) of a specific volume in the computer the agent is running on. The path corresponds to the device, e.g. "/dev/hda1".
  • textFile: { path: "file path" }: Captures the contents of a text file; e.g. a log file, and transmits it as a data source; which can be used as any other robot attribute or visualized in a Custom Text File widget. The file is monitored for changes.
  • imageFile: { path: "file path" }: Captures the contents of an image file; transmits it as a data source. The image can later be visualized in a Custom Image widget. The file is monitored for changes.
  • derived: { transform: "an expression", filter: "an expression" }. Defines a data source as "derived" from others, by means of the expression transform which can use other data source values; for example to select one specific sub-field of a complex JSON object, or to combine multiple data sources into one. An optional filter (another expression) can also be given to skip updating the data source when the filter evaluates to false (or 0). See the expression language reference.

A DataSourceDefinition may also contain a timeline field to specify if the data should be stored in a time-series database for visualization in different widgets (e.g. Timelines, Event Logs or History). If the data is not numeric, timeline must contain fieldType specifying the type of data to store.

Configuring: Status Definitions

Status Definition Configuration Object Schema

{
  "metadata": {
    "scope": "account",
    "id": "<statusId>"
  },
  "apiVersion": "v0.1",
  "kind": "StatusDefinition",
  "spec": {
     "calculated": {
       "expression": "<expression>",
       "filter": "<expression>"
     },
     "rules": [
       {
         "function": "ABOVE/BELOW/EQUALS/NOT_EQUALS/CONTAINS",
         "params": [],
         "sustainedForSeconds": 120,
         "status": "OK/WARNING/ERROR"
       }
     ]
  }
}

Example status definition monitoring a motor temperature sensor

{
  "metadata": {
    "scope": "account",
    "id": "motorTemperatureId"
  },
  "apiVersion": "v0.1",
  "kind": "StatusDefinition",
  "spec": {
     "rules": [
       {
         "function": "ABOVE",
         "params": [70],
         "status": "WARNING"
       },
       {
         "function": "BELOW",
         "params": [85],
         "sustainedForSeconds": 60,
         "status": "ERROR"
       }
     ]
  }
}

Status definitions allows setting up rules to flag different problems in robots. For example, a battery level dropping below a threshold, a sustained low motor speed, or a specific data source receiving an unexpected value. Statuses are visualized in Fleet dashboards. and also serve as the source for defining Incidents -- see next section.

Each Status may take three values: OK, WARNING and ERROR. They are displayed in different colors in dashboards. To determine these values, a StatusDefinition is configured with a number of rules: the first one to match the current robot condition determines the status value.

Each rule contains the status value to assign (OK, WARNING or ERROR). The rule is defined by a function, one of EQUAL, NOT_EQUALS, BELOW, ABOVE or CONTAINS, which can be combined with an optional sustainedForSeconds. The sustainedForSeconds value ensures the rule is only triggered after the conditions are met for the specified amount of time. The argument(s) for the function are set in params, as a list of values. For example, to indicate if the temperature has been greater than 85 for over a minute, one would define { "function": "ABOVE", "params": [85], "sustainedForSeconds": 60 }.

A Status can be evaluated based on a data source value (see Data Sources). In this case, the <statusId> must match an existing data source id (and does not have a calculated object in its spec. In the previous example, the statusId would be the ID of motor temperature's data source. Alternatively, an advanced StatusDefinition can be defined using an arbitrary expression, using multiple data sources or robot tags. In this case, the calculated field must contain:

  • A expression string in InOrbit's expression language. This allows evaluating an arbitrary condition, such as "If a robot with the 'prod' tag is not moving and its current program is 'delivery'".
  • Optionally a filter can determine if the status is to be computed. This allows skipping computation. For example, when using a rich JSON data source as an input, certain messages can be skipped and will not update the status.

Expressions can use values from any robot data source, combine them with comparisons and logical operators, use aggregations on data sources, and even localization-related functions. See expression language reference below.

Note: The number of advanced StatusDefinitions is limited in Free Edition. See pricing page.

Once a Status changes its value to Warning or Error, this can trigger an incident. Refer to IncidentDefinition config objects below.

Configuring: Incident Definitions

Incident Definition Configuration Object Schema

{
    "kind": "IncidentDefinition",
    "apiVersion": "v0.1",
    "metadata": {
        "id": "string",
        "scope": "string"
    },
    "spec": {
        "statusId": "string",
        "label": "string",
        "warning": {
            "notificationChannels": [],
            "autoActions": [],
            "manualActions": [],
            "severity": "string"
        },
        "error": {
            "notificationChannels": [],
            "autoActions": [],
            "manualActions": [],
            "severity": "string"
        },
        "ok": {
            "notificationChannels": [],
            "autoActions": [],
            "manualActions": [],
        }
    }
}

Example incident definition: Both at Warning and Error status levels, this incident is notified in the app with an option to run a previously defined action RunScript-123456. In the Error the incident is also notified in a Slack channel, and another action is automatically run.

{
    "kind": "IncidentDefinition",
    "apiVersion": "v0.1",
    "metadata": {
        "id": "batteryLow",
        "scope": "account"
    },
    "spec": {
        "statusId": "batteryLow",
        "label": "My Battery Incident",
        "warning": {
            "notificationChannels": ["app"],
            "autoActions": [],
            "manualActions": ["RunScript-123456"],
            "severity": "SEV 2"
        },
        "error": {
            "notificationChannels": ["app", "slack#support"],
            "autoActions": ["PublishToTopic-7890ABC"],
            "manualActions": ["RunScript-123456"],
            "severity": "SEV 0"
        }
    }
}

Incident definitions allows setting rules about how and when incidents are triggered and notified based on the configured statuses (see StatusDefinition). They define how these incidents must be notified, their severity levels, associated actions, and more.

Each incident definition has the following main properties:

  • id: Identifies the definition.
  • statusId: The ID of the status definition that triggers this incident.
  • label: A descriptive label for the incident.
  • warning: Defines rules to handle the case when the associated status is in "warning" state. Including the notification channels, severity level and associated actions.
  • error: Defines rules to handle the case when the associated status is in "error" state.
  • ok: Defines rules to handle the case when the associated status switches to "ok" state.

The alerts for an incident can be routed through different notification channels. The actual channels depend on the integrations configured in the account. Available options are:

  • InOrbit application: app
  • Slack: slack#...
  • Google Chat: googleChat
  • OpsGenie: opsgenie
  • Webhook: webhook

For example, to be notified in the App as well as in a Slack channel called #alerts, the notification channels value would be: notificationChannels: ["app", "slack#alerts"].

Finally, the valid severity levels are: SEV 0, SEV 1, SEV 2, and SEV 3. These do not apply to ok states.

When an incident is triggered, there is the option to automatically run Actions on the robot. These are the actions listed (given their IDs) in the autoActions field. Actions can also be an option for the user to be executed as a response to an incident notification (in the app, Slack or any other notification channel). For example, an action "Return to dock" could be one possible response from an operator to a low-battery incident, after the user determines if there is enough battery left to complete the current task. These actions are listed in the manualActions field. Refer to the next section on the ActionDefinition kind to configure actions. Note that the REST API can also be used to get the lists of valid status and actions IDs.

Find the full specification of the IncidentDefinition kind in JSON schema format at io-config-as-code repository.

Advanced Incidents

Combining different status, incidents and actions create a powerful tool for monitoring a robot fleet, including notifications through different channels for issue management (e.g. OpsGenie, Zendesk) and automation of common operations (e.g. L1 support).

This section includes complex examples that include all these pieces working together.

Example: Faulty Dock

Example to detect a failed docking attempt

[
  {
    "metadata": {
      "scope": "account",
      "id": "dockedNotCharging"
    },
    "apiVersion": "v0.1",
    "kind": "StatusDefinition",
    "spec": {
      "calculated": {
        "label": "Charging issues",
        "expression": "getValue('dsDockedStatus')==1 and not getValue('dsCharging')",
      },
      "rules": [
        {
          "function": "EQUALS",
          "params": [true],
          "status": "WARNING",
          "sustainedForSeconds": 30,
        },
        {
          "function": "EQUALS",
          "params": [true],
          "sustainedForSeconds": 120,
          "status": "ERROR"
        }
      ]
    }
  },
  {
      "kind": "IncidentDefinition",
      "apiVersion": "v0.1",
      "metadata": {
          "scope": "account",
          "id": "batteryIncident"
      },
      "spec": {
          "statusId": "dockedNotCharging",
          "label": "Docked and not charging",
          "warning": {
              "notificationChannels": ["app", "slack#incidents", "opsgenie"],
              "autoActions": ["script-action-retry-dock"],
              "severity": "SEV 2"
          },
          "error": {
              "notificationChannels": ["app", "slack#support"],
              "manualActions": ["script-action-retry-dock"],
              "severity": "SEV 1"
          }
      }
  }
]

We will assume the account has two data sources configured: dsDockedStatus (with a numeric value with 1 indicating "successfully docked") and dsCharging (a boolean value indicating that the main battery receives charging voltage). They indicate if the robot is currently docked in its charging port, and if the battery is currently charging (receiving actual voltage), respectively. These Data Sources could be sourced from ROS data, SDK or API calls, etc. -- see reference.

A plausible scenario for robots is that they dock, but there is either a faulty connector in the dock or some other electrical problem preventing their batteries from charging (dsCharging is false).

Each of the data sources' values is not indicator of a problem by themselves, but the combination of "docked" and "not charging" is a critical problem, as the robot will eventually run out of battery even if operators were around, not noticing is not really charging.

To detect and attempt to the situation, we provide a sample configuration (in the side panel) with the following features:

  1. The defined status dockedNotCharging is computed from an expression combining both values dsDockedStatus and dsCharging with boolean logic. The expression will take the value true when the robot reports itself as docked and is not charging. See expressions reference for other operators and functions.
  2. The StatusDefinition contains two rules: it will switch to WARNING state after 30 seconds (alerting, but allowing the situation to resolve itself) and turn to ERROR state after two minutes.
  3. There is also an IncidentDefinition defining the notifications for this situation:

    a. On WARNING level a notification is only displayed in the app (in case operators are online). Additionally, an action `script-action-retry-dock' (refer to Actions reference) is automatically executed. This action could execute an action to make the robot push itself a bit more into the dock, in case the problem were loose electrical contacts in the dock port.

b. On ERROR level there are no actions executed, but the alert is propagated to several channels. First, the app notifications is updated. The same alert goes to Slack to a channel called "#incidents", and is also sent to OpsGenie. (Refer to Integrations documentation). All these notifications include a link to jump to the app, directly to this robot's dashbboard (this is a built-in feature). They will additionally have a button to re-try executing the same script-action-retry-dock action (which can be executed directly from the Slack message).

Note that if the WARNING level had resolved the situation (which is desirable, as the problem gets resolved automatically from InOrbit cloud), the event is still logged and will be found in the Audit Logs for this robot.

Complex example including detecting a robot unable to navigate towards its goal

[
  {
    "metadata": {
      "scope": "account",
      "id": "unableToNavigate"
    },
    "apiVersion": "v0.1",
    "kind": "StatusDefinition",
    "spec": {
      "calculated": {
        "expression": "(getValue('dsMission') in ['DELIVERY','RETURN-DOCK']) ? visitedAreaDiagonal(60) : null",
      },
      "rules": [
        {
          "function": "BELOW",
          "params": [2],
          "sustainedForSeconds": 600,
          "status": "ERROR"
        }
      ]
    }
  },
  {
      "kind": "IncidentDefinition",
      "apiVersion": "v0.1",
      "metadata": {
          "scope": "account",
          "id": "navigationProblem"
      },
      "spec": {
          "statusId": "unableToNavigate",
          "label": "Unable to reach destination",
          "error": {
              "notificationChannels": ["slack#support", "webhook"],
              "severity": "SEV 2"
          }
      }
  }
]

In this example a delivery robot is configured with a data source dsMission with string values representing the current task type, which could include going to pick up an item, performing delivery tasks, returning to the doc or charging in the dock.

Some of these missions require the robot to autonomously navigate. Losing the ability to reach a navigation goal is one of the most common causes of problems in a robot -- for whatever cause (obstacles in a planned route, doors not opening, wheel malfunctions, etc.).

This situation can be detected by two conditions:

  • If the value of dsMission includes any of the known mission values, the robot is known to be navigating. In the example we include "DELIVERY" to represent carrying an element to a destination, and "RETURN-DOCK" when navigating back to its dock to recharge. The .includes() function determines if the list contains the actual value returned by getValue() --
  • The function visitedAreaSide() returns an approximation of the area navigated by the robot during the last N seconds (60 seconds in the example).

The two values are combined with a ternary conditional: If the robot is navigating, its value is the value of visitedAreaSide(); and otherwise it is simply null (no need to worry about speed or travelled distance if the robot was on its dock, for example).

Refer to expressions language reference for a detailed usage of these and other functions in StatusDefinition expressions.

The robot might not get to a full stop if it keeps retrying to avoid an obstacle, or if its motors or wheels are working too slowly; so using a simpler data source such as "linear distance" is not representative of the actual problem.

The StatusDefinition rule monitors the robot with plenty of time (five minutes) to get momentarily stuck from another nearby robot or to wait for elevators doors to open, for example. But once this time is elapsed, it turns into ERROR state.

In the ERROR, an immediate alert is sent through Slack, and also triggers a webhook call: If this is configured in the account, it can be hooked to an internal or third-party API, for example to send a SMS to field operators.

Configuring: Action Definitions

Action Configuration Object Schema

{
    "kind": "ActionDefinition",
    "apiVersion": "v0.1",
    "metadata": {
        "id": "string",
        "scope": "string"
    },
    "spec": {
        "label": "string",
        "description": "string",
        "confirmation": {
            "required": "boolean"
        },
        "widgets": ["string"],
        "arguments": [{
            "name": "string",
            "type": "string",
            "value": "any",
            "dataSourceId": "string",
            "input": {
                "control": "",
                "values": [{
                    "label": "string",
                    "value": "any"
                }]
            }
        }]
    }
}

Action definition example, using the value of a data source as an argument for a script execution.

  {
      "kind": "ActionDefinition",
      "apiVersion": "v0.1",
      "metadata": {
          "id": "CancelCurrentMission",
          "scope": "account"
      },
      "spec": {
          "type": "RunScript",
          "label": "Cancel Mission",
          "arguments": [
              {
                "label": "filename",
                "value": "mission.sh",
              },
              {
                "label": "filename",
                "value": "--cancel",
              },
              {
                "label": "--mission-id",
                "dataSourceId": "MissionDataSourceId",
              }
          ]
      }
  }

This example would publish a message to a ROS topic, for example "START_DELIVERY|4|404", formatted using the current values of two robot data sources.

  {
      "kind": "ActionDefinition",
      "apiVersion": "v0.1",
      "metadata": {
          "id": "Start Delivery",
          "scope": "account"
      },
      "spec": {
          "type": "PublishToTopic",
          "label": "Start Delivery",
          "arguments": [
              {
                "label": "message",
                "value": "START_DELIVERY||",
              },
              {
                "label": "floor",
                "dataSourceId": "FloorNumber",
              },
              {
                "label": "room",
                "dataSourceId": "RoomNumber",
              }
          ]
      }
  }

The ActionDefinition kind allows configuring robot Actions. Actions are commands sent to the robots. By configuring actions, operators can execute commands on the robots. Actions can be hooked to incidents to be automatically run or offered as options for operators to manually run when an incident occurs. Learn more.

There are various types of actions and their configuration options vary depending on this type as well on the way they should run. The following is a reference for all configuration fields. Note that some options are only available to specific Editions, see pricing page.

  • id: Identifies the action. The number of user-defined actions can be limited in some editions.
  • type: The action type, including executing scripts, publishing data to ROS topics, etc. (See below)
  • label: A label to display in dialogs and buttons when this action is about to run. It is recommended to keep it short (one or two words).
  • description: A more comprehensive description of the action, that will be displayed when action arguments are about to be input by a user.
  • confirmation: Determines if the action requires an extra confirmation from the user before being executed. This confirmation will occur only if required: true is part of this object.
  • arguments: A list of action arguments. These may be necessary depending on the action type and if its execution depends on user input or the robot state -- See next sections.
  • widgets: A list of widgets this action should appear on for quick access. The only allowed value in this version is "navigation".

Action types

There are different action types to perform a number of robot actions. Each action type may expect some pre-defined arguments.

  • PublishToTopic: Publishes the value of message argument through the /inorbit/custom_commands topic. Its string value may contain replacements, see below.
  • RunScript: Executes a shell script (indicated by filename argument) which must exist in the ${HOME}/.inorbit/local/user_scripts directory in the robot. Any additional argument specified in the argument sent as argument to the script.
  • InOrbitPage: Defines an action to jump to a specific page or section in the InOrbit app, as defined by the path argument. It is only available through the app in widgets and notifications (in app or notification channels).
  • Url: Allows inserting a link to jump to an arbitrary (external) page, e.g. a third party incident management tool. The url argument defines the page, which may contain replacements, see below.

Arguments and User Input

Each action type depends on specific arguments to be executed.

Some actions arguments, listed in the previous section, allow performing replacements. For example, one could publish a message (via PublishToTopic action type) which includes the value of a given data source. To use this feature, define additional arguments and include their label within double curly brackets. This variable will be replaced with the value in the main argument. See examples below.

Alternatively, arguments could be input by users at the time the action is executed. This feature is only available to some Editions. Defining an input element in at least one argument, causes a dialog modal to pop up before the action is executed in order to set the argument values.

The accepted values for input are "text" and "select". The first one prompts a user with a text entry dialog. The second one expects an additional property, values, to include the list of options from which the user can select this value. Each option must be an objet with { label, value }: The value is sent to the action execution, while the label is only for display at the dialog when for the user to know what the option is about.

Actions Examples

The first definition given as example defines an action that, when run, executes a script mission.sh from the scripts directory ${HOME}/.inorbit/local/user_scripts, passing the arguments: --cancel --mission-id <aValue>, where <aValue> is the current value of MissionDataSourceId data source.

Note that some arguments have a label, passed to the script execution, and others do not. Also, some arguments may have a fixed value ("mission.sh" or "--cancel") and others take the value of a data source (MissionDataSourceId). Alternatively, the actual value can be user-provided, when using the input field (only available to some Editions).

The second example defines a PublishToTopic action, which publishes a message to a ROS topic (its type is a string: std_msgs/String).

Such message can be defined in advance in the message argument, or it can also be composed using replacements from data sources values. In the example, the data sources FloorNumber and RoomNumber, are data points for the destination of a robot hotel on a delivery mission. They are concatenated to form a single string message that a ROS node in the robot could receive and use to start a mission.

Configuring: Robot Camera

Cameras Configuration Object Schema

{
  "kind": "RobotCamera",
  "apiVersion": "v0.1",
  "metadata": {
    "scope": "<scope>",
    "id": "<cameraId>"
  },
  "spec": {
     "label": "front",
     "shortLabel": "F",
     "rosTopic": "usb_cam/image_raw",
     "width": 320,
     "height": 240,
     "rate": 1,
     "outputEncoding": "rgb8",
     "quality": 20,
     "rotation": 0,
     "mirror": false,
     "enabled": true
  }
}

The RobotCamera kind allows configuring cameras with still images transmitted from robots. This allow users to set and adjust parameters for their camera feeds.

Each camera configuration has the following properties:

  • id: Identifies the camera.
  • label: Label displayed on screen
  • shortLabel: Optional short label to replace the default label (normally the first letter of label)
  • rosTopic: The ROS topic
  • width: Integer of width in pixels
  • height: Integer of height in pixels
  • rate: The number of frames per second
  • outputEncoding: Encoding type, default is "rgb8"
  • quality: Integer of quality between 0 and 100
  • rotation: Rotation of the camera feed in multiples of 90
  • mirror: Optional boolean for mirroring image. Default is false
  • enabled: Boolean for enabling camera. Default is true

Configuring: Mission Tracking

Mission Tracking config schema

{
  "metadata": {
    "scope": "<scope>",
    "id": "all"
  },
  "apiVersion": "v0.1",
  "kind": "MissionTracking",
  "spec": {
    "processingType": "api|derived",
    "attributes": {
      "attributeId1": [
        {
          "type": "string",
          "path": "optional string",
          "values": ["value1", "value2"],
          "acceptBeforeStart": 0,
          "acceptAfterEnd": 0
        }
      ],
      "attributeId2...": []
    },
    "missionFields": {
      "field1": {
        "defaultInitialValue": "value",
        "defaultFinalValue": "value",
        "index": false
      },
      "field2...": {}
    },
    "stateDefinitions": {
      "state1": {
        "defaultStatus": "ok|error|warn",
      },
      "state2": {}
    },
    "execution": {
        "modes": {
            "start": "tagId",
            "end": "tagId"
        },
        "useLocks": true,
        "waypoints": {
            "distanceTolerance": 1,
            "angularTolerance": 1,
        }

    }
  }
}

Mission Tracking allows users to observe and measure their robots' performance while they execute their assigned tasks. Missions vary for every fleet of robots, and InOrbit's API is a generic model to accomodate this. A Mission could represent picking objects, delivering items, cleaning areas, etc.

Note: Mission Tracking is an advanced feature available only in certain editions. Please contact us for questions, pricing and help to get started.

This section details the Config API schema. For details on Mission Tracking concepts, refer to Mission Tracking documentation.

Note that unlike most other APIs in this section, Mission Tracking applies to all missions in general. For this reason, the only accepted id value is "all".

The configuration schema has many different options to accommodate a wide range of Missions. Most fields are optional; and some accept shorter versions for brevity:

  • processingType indicates if Missions are defined via API calls (processingType: api) or "derived" through data source updates (processingType: derived). API is the recommended processing type. Mission updates are sent by the agent on one data source in the form of JSON-encoded objects. These objects are translated into REST API calls to the Missions REST API, and must adhere to its schema. The second case defines one specific data source to act as "boundary" for Missions. When this data source emits a specific value (see attributes field) then a mission is assumed to start; and it ends when the data source switches back to a different value. Normally, only one method is used per account; but they can be combined by setting processingType to an array containing both processing type values.

  • attributes specifies how to map attribute updates to Missions. This is an object whose keys are attribute ids, and the values are lists of mappings. (In the case of using only one mapping, which is the most common scenario, an object value within an attribute key is also accepted --see example). Each element should contain the type of this mapping. The most important one is the "mission" type, defining which attribute contains the JSON-encoded API calls or the Mission boundary values (for "api" and "derived" processing types, respectively). More details on attributes are given below.

  • missionFields configures some special behaviors to Mission fields such as state. It can set default initial and ending values which are used if the mission did not receive values to update them.

  • stateDefinitions allows giving more semantics to the state mission field, enabling each state value (a free-form string, although some common states are normally used) to be mapped to a status (one of "ok", "error" and "warn", which are used to determine some metrics when aggregating missions).

  • execution allows adding some options that define how missions are going to be executed, adjusting some default behaviors for missions.

Attributes

Each of the objects in attributes define a mapping from a data source (or a portion of it) to a mission field. Each of these objects accept the following fields:

  • type: The semantics of the mapping is first defined by this field, one of:

    • "mission": In processingType = "api", this is the data source containing JSON API call updates. In processingType = "derived", this is the mission boundary.
    • "label": Defines a mission name, viewable in the mission widgets and reports.
    • "state": Defines the current mission "state", such as "in-progress", "completed", "stuck", etc. Any string value is accepted and their meaning is only assigned by the user.
    • "status": Defines the current mission status: "ok", "error" or "warn", which is used to determine if the mission was successful when building mission reports.
    • "data": Defines a mission metadata field. In addition to mission fields defined in the REST API schema (label, state, status, etc.), any number of data elements can be added within a data object as metadata. See label field above.
    • "percent": Defines the mission completion percentage; given as a float number between 0 and 1.
    • "startTs": Defines the start timestamp (in epoch milliseconds) of the mission. It is not necessary to define one; as otherwise the start timestamp is assumed to be the time of the mission creation.
    • "endTs": Similarly to startTs, for the mission end time.
  • values: Only used for the "mission" attribute. Defines which values represent that the robot is currently running a mission. For example, ["delivery"].

  • path: If the attribute is typed as "json" (see Data Sources), then a specific sub-element of it can be selected to determine the mapping. For example, if a data source reports an update of a state machine such as { navGoalId: 1234, currentTask: 'delivery', targetRoom: 101 }, then a path value "currentTask" could select the "mission" attribute and a path value targetRoom could be mapped to a data element -- defining two different mappings for the same attribute.

  • label: Only used for data-typed attributes. The data object in missions is a key-value dictionary. The keys are defined by the label of the attribute.

  • acceptBeforeStart and acceptAfterEnd allow receiving mission field updates not strictly within the time window where the mission attribute takes the values that define a mission. This is used since attributes could be published (and received in the cloud) within some seconds difference, and also because mission plans or mission summaries could be published before or after the mission. These optional values are a number of seconds to allow to wait for additional attribute updates (acceptAfterEnd) or to look for previous updates (acceptBeforeStart) when a mission starts.

Execution

The following fields define some behaviors for missions execution, fields that indicate if robots are going to be locked by the missions when they start and finish running, among other options.

  • modes: Defines which modes (tags) are going to be assigned to a robot when it starts or ends the mission execution. The Ids of the tags can be retrieved by using the InOrbit CLI.
    • "start": Id of the tag (it needs to be a Mode) that you want to assign to the robot when it starts executing a mission.
    • "end": Id of the tag (it needs to be a Mode) that you want to assign to the robot when it ends executing a mission.
  • useLocks: Boolean. If true, the robot will be locked by the InOrbit service user every time it starts executing a mission and unlocked when it finishes the execution.
  • waypoints: Defines tolerances for waypoints, if the pose of the robot is within the tolerated values, assumes that the goal of the waypoints has been reached.
    • "distanceTolerance": A float value that tells how much distance will be tolerated to indicate that a waypoint was successful
    • "angularTolerance": A float value that tells how much angle will be tolerated to indicate that a waypoint was successful

Example

Mission Tracking complex example

{
  "metadata": {
    "scope": "account",
    "id": "all"
  },
  "apiVersion": "v0.1",
  "kind": "MissionTracking",
  "spec": {
    "processingType": "derived",
    "attributes": {
      "stateEvent": [
        {
          "type": "mission",
          "path": "currentTask",
          "values": ["delivery"]
        },
        {
          "type": "data",
          "path": "targetRoom",
          "label": "Room"
        }
      ],
      "navigationStatus": {
        "type": "state",
        "acceptBeforeStart": 20
      },
      "pathCompletion": {
        "type": "percent",
        "acceptAfterEnd": 60
      }
    },
    "missionFields": {
      "state": {
        "defaultInitialValue": "planning"
      }
    },
    "stateDefinitions": {
      "elevator-issue": {
        "defaultStatus": "warn",
      }
    }
  }
}

In this example, a json-typed attribute is used for two purposes. Whenever its currentTask field has value "delivery", this marks the mission start. Then, the value of targetRoom field is added to the mission metadata. Thus, the config part of this attribute is a two-element array. All other attributes contain a single object, as they are mapped to only one mission field.

Each mission initial state is "planning", a user-defined value. The missions will last as long as the currentTask value is "delivery". During that time and up until 20 seconds after it switches to a different value, any value of navigationStatus attribute will be used as the mission state. A user-defined state "elevator-issue" is assigned a "warn" status, meaning it will be colored accordingly (yellow) in widgets and reports, and the mission would not be considered successful if this is its final state.

Also within the mission duration and one minute after it ends, the value of the pathCompletion attribute is used as completion percentage.

Configuring: Mission Definitions

Status: Early Access. Note: These features are not available in all Editions. See pricing page.

Mission Definition config

apiVersion: v0.1
kind: MissionDefinition
metadata:
  scope: account
  id: "<missionId>"
spec:
  label: Mission name
  steps: # The sequence of steps to complete the mission
  - label: "Setup mission data"
    data:
      # Sets mission's metadata. Also available through Mission Tracking APIs,
      # from robot-reported data
      itemType: "bottle"
      weight: 1.23
      containerNumber: 1
  - label: "Go to drop off location"
    waypoint:
      # Navigating to a waypoint, given its coordinates (see Named Waypoint example below)
      x: 1.10
      y: 20.98
      theta: 1.23
      frameId: "map"
    timeoutSecs: 300
  - label: "Wait 5 seconds"
    # A simple wait
    timeoutSecs: 5
  - label: "Open lid"
    runAction:
      # Runs an action defined in the robot
      actionId: script-open-lid
      arguments:
        ledColor: "#ff00ee"
        lidNumber:
          _data: "containerNumber" # Refers to values stored in "data" object
  - label: "Wait for lid to be closed"
    waitUntil:
      # Evaluates an expression and waits until it has a True value
      expression: getValue("lid_open") == 0
      timeoutSecs: 60
    completeTask: "Pickup"
  - label: "Go to dock"
    # Navigates to a pre-defined, named waypoint
    waypoint: "dock"
    completeTask: "Delivery"
  selector:
    robot:
      # The selector allows restricting which robots can execute the mission.
      # Criteria include robot tags, inclusion lists or name patterns.
      tagIds: ["delivery_bot"]

InOrbit allows configuring what missions are for your robot fleet, as well having them executed by your robots: whether these are repeated, pre-defined missions or one-off missions that trigger a one time task. This is achieved through the MissionDefinition kind and the Mission Dispatch APIs

Note that these definitions work together with mission tracking, which works both on missions defined through InOrbit as well as missions executed by any proprietary software in the robot that are reported to InOrbit through the Mission Tracking APIs. Some parameters to tune mission execution and mission tracking can be adjusted using the MissionTracking kind.

Missions are defined using the MissionDefinition kind. Each mission definition is identified by a unique missionId and uses a human-readable label or name. Missions consist of a series of steps to be executed sequentially, and may also contain a selector to define which robots are allowed to execute it.

Steps can be of several types. All steps accept a label to describe the step (which is displayed in mission widgets), and an optional timeoutSecs property to define a maximum wait in seconds for the step to execute.

The field completeTask allows to define a "task" to be marked as complete when this step finishes execution. This is displayed in Mission widgets, and useful to understand the overall progress of a mission consisting of many (small) individual steps. For example, after going to a drop-off location, opening a lid and waiting for a user confirmation (3 mission steps) a single task "Delivery" could be mark as completed.

The available mission steps are:

  • Navigating to a waypoint: If a waypoint property is present. This field can contain an object with { x, y, theta, frameId } representing the target pose to navigate to. The frameId is optional; used to reference sub-locations (e.g. different floors). Alternatively, a single string of the form "<annotationId>" can be used to refer to existing, named waypoints defined in the robot location (defined through the Annotations API or in the Navigation widget),

  • Executing an action: A mission step can consist of executing an arbitrary action in the robot. This action could represent turning on a rotary brush, opening a lid to allow a user to pick up an object, etc. These steps contain an actionId field, referring to an already defined ActionDefinition: they may be implemented through scripts installed in the robot, publishing messages to ROS topics or other action types; see action definitions. The optional arguments object define the action argument values. They may be constants (numbers, strings) or refer to mission fields within the data (mission metadata created by user) or arguments (mission metadata passed while dispatching the mission). A single object such as { _data: "<dataKey>" } within arguments will get replaced by the data value at the moment the step is to be started. Similarly, a key _arguments is used to fetch values from the initial mission arguments. If enabled, actions can also be run on a different robot, for scenarios where multiple robots or devices need to be orchestrated. This uses the target mechanism -- see "Waiting for a condition" below.

  • Updating mission's data: At any point during the mission (specially at mission start, or after completing intermediate goals) the mission metadata (see Mission Tracking) can be updated. This metadata is a simple dictionary of key-value objects, containing whatever values represent the mission data: product ids, route name, order numbers, customer names, etc. This step type is defined with a data field, containing an object with the mapping from metadata field names to their values.

  • Waiting for a condition: A mission step may consist of simply waiting for a condition to happen. This is defined with a field waitUntil. These steps contain an expression field. The step will wait until the expression evaluates to true. See expressions language to check the supported expressions. Just as when running actions, conditions can also be evaluated on another robot, not the one executing the mission. For this, use { target: { robotId: "<robotId"> } } in the waitUntil step. This is also supported when running actions.

  • A simple wait of a predefined number of seconds. This step only uses the timeoutSecs property.

Mission Selector schema

{
  "robot": {
    "tagIds": ["picker-robots", "firmware-023"],
    "robotIds": ["picker001", "picker002"],
    "robotId": "picker001",
    "namePatterns": ["picker.*"]
  }
}

The selector property is used in mission definitions to restrict what robots can execute the mission. It consists of a single property robot with different ways to define what robots are accepted. If multiple conditions are present, all of them must hold for a robot to execute the mission. Conditions are defined by fields:

  • tagIds: Defines a list of tags (given by their ids) that the robots must have at the time of dispatching the mission. Its value must be list of strings.
  • robotIds: Explicitly enumerates which robots (given by their ids) can execute the mission. Any of these robots can be selected. Its value must be list of strings.
  • robotId: Similar to robotIds, as a shorter form to define a single robot by an id.
  • namePatterns: Allows defining one or more regular expressions that will need to match the robot name for a robot to be selected for the mission.

Concepts

Expressions Language

The following examples assume some data sources and tags exist for the robots; the data source ids are given as example and should be self-explanatory.

Example: Determines the maximum value of a "signal strength" data source and takes it logarithmic value. This value could be compared using ABOVE or BELOW rules in a StatusDefinition.

log10(maxValue('signalStrength'), 600))

Example: Detect a failed docking attempt. If given as part of a status rule and it is configured with a { sustainedForSeconds: 180 }, it would trigger if the robot has not docked after three minutes of changing its program to "DOCKING"

getValue('currentProgram')=='DOCKING' and not getValue('is_charging')

Example: Robot stopped while on a mission. As part of a status rule it triggers if a robot has not left a 2-meter square for three minutes, signaling it is unable to navigate through its path.

hasTag("prod") and getValue('currentProgram')=="DELIVERY" and visitedAreaSide(180)<2

Example: This example expands on the 'DataSourceDefinition' example "motorTemperatureId" above. The 'StatusDefinition' example below shows how absent motor temperature readings could generate an alert after 120 seconds:

[
  {
    "metadata": {
    "scope": "account",
    "id": "motorTemperatureId"
  },
    "apiVersion": "v0.1",
    "kind": "StatusDefinition",
    "spec": {
      "calculated": {
        "expression": "getValueAgeMs(motorTemperatureId)>=120000"
       },
      "rules": [
         {
           "function": "EQUALS",
           "params": "true",
           "status": "WARNING"
         }
      ]
    }
  }
]

Example: Given an event JSON object attribute, if the robot is seeing one of the configured landmarks, this expression would take a "lobby" value, if not it default to the "current_floor" data source instead. Note that the expression works in multiple lines or a single line.

event = getValue(event);
landmarks = event and event.navigation ? event.navigation.visible_landmarks : [];
("L00" in landmarks) or ("FRONT_DOOR" in landmarks) ? "lobby" : getValue("current_floor")

Certain parts of InOrbit's advanced configuration allow definining values (such as conditions) from a combination of comparisons, boolean logic, and functions using robot's information (data sources, tags, localization data). See for example StatusDefinitions.

This language will look familiar to anyone versed in programming languages, and closely resembles JavaScript language syntax.

Its very rich expressivity allows defining complex expressions, only limited to the data available about the robot in InOrbit platform. It creates a very powerful tool for expressing robots conditions for each domain's business rules.

Here we briefly describe its syntax and operations.

  • Constants includes numbers in decimal notation, strings enclosed in single or double quotes, the boolean values false and true and null.
  • Arithmetic and logical operations include +, -, * (multiplication), / (division), % (remainder), ^ (exponentiation) as well as not, and, or (boolean operators).
  • Comparison includes ==, !==, >=, <=, > and < (equals, not equals, etc.)
  • All sub-expressions can be grouped using parenthesis ( and ) to make precedence explicit.
  • Arrays (list of values) are accessed through subscript operator [], e.g. a[3]. They can be concatenated with ||.
  • Functions calls are denoted using their name, followed by parenthesis with optionally argument values separated by commas. For example, minValue("battery", 30).
  • Variables can be assigned with = to reuse values; and these assignments are separated using the ; delimiter. For example x=3; y=4; sqrt(x*x + y*y).

Spaces are optional, operator precedence and associativity follow usual arithmetic conventions. More formally, precedence is as follows:

  • (...): grouping
  • f(), a[i]: Function calls, array indexing (left-associative)
  • ^: Exponentiation (right-associative)
  • +, -, not, sqrt unary operators (full list below)
  • *, /, %: Product, division and remained
  • +, -, ||: Addition and subtraction (arithmetic) and concatenation (strings and lists)
  • ==, !=, >=, <=, >, <, in: Comparison operators and inclusion operator (x in a means "is element x included in list a)
  • and: Logical AND
  • or: Logical OR
  • x ? y : z: Ternary conditional: if x then y else z.
  • =: Variable assignment
  • ;: Expression separator

The unary operators include in addition to + and - (sign), many algebra and trigonometry functions:

  • abs (absolute value), -ceil, sign, round, trunc (sign, rounding and truncation),
  • sqrt and cbrt (square and cubic root)
  • ln, log10, log2 (logarithms)
  • sin, cos, tan, tanh, asin, acos, atan, atanh (trigonometric functions)
  • length (array length)

All of these are unary operators, although its equivalent function syntax is usually found, e.g. acos(x).

Additionally, robot-specific functions allow evaluating conditions on robot-reported data:

Language functions

  • get(<value>, <field>, <defaultValue>): Given an object, it returns the value of one of its fields. Normally used for robot attributes of type "JSON". If <value> is not an object, or it does not containt the given <field>, it returns <defaultValue> (which is optional, when omitted it is assumed to be null).

Robot Data Functions

  • getValue(<ds>): The current value of the robot data source identified by <ds>.

  • getValueAgeMs(<ds>): The age of the robot data source identified by <ds> in milliseconds; defined as the time that has passed since this data source value was last sent by the robot.

  • minValue(<ds>, <secs>), maxValue(<ds>, <secs>), meanValue(<ds>, <secs>): Aggregation operators to obtain the minimum (resp. maximum, average) value of the data source identified by <ds> during the last N seconds.

  • hasTag(<tag>): Determines if a robot contains a tag -- See Collections

  • distinctValues(<ds>, <seconds>) and sustainedValue(<ds>, <seconds>): Determine all unique values of a given source <ds> for the last <seconds> time window. The function distinctValues returns all these values in a list (see array operators above). The function sustainedValue can be used to know if the data source has reported one unique value over that time: in that case it returns this value; otherwise it returns null.
    Both functions require enabling storage of values history. Refer to DataSourceDefinition in StatusDefinitions.

Localization Data Functions

  • visitedAreaDiagonal(<seconds>): The diagonal length of the smallest bounding box containing all of the robot positions during the last N seconds. It allows determining if the robot is actually moving; beyond its linear or angular distance (e.g. the robot can be moving in circles attempting to go around an obstacle).

  • visitedAreaSide(<seconds>): Similar to visitedAreaDiagonal, except it reports the largest side of the smallest bounding box containing all of the robot positions during the last N seconds.

  • match(<regularExpression>, <variable>): Performs regular expression matching. The result is null if there is no match, or a list of strings when matched (which can also be used as "true" value for a boolean expression). This list contains the entire match as first element, and any captured groups as subsequent elements. Regular expressions follow ECMASCRIPT language specification, refer to this cheatsheet. Character classes, grouping and other regular expression features are supported. For example, using regular expressions to test for a data source and extract part of its value: Using x = getValue("dsId"); m = match("NAVIGATE-(.*)", x); m ? m[1] : "no goal" will test the value of a data source dsId against the regular expression NAVIGATE-(.*). Let's say the value of this data source is "NAVIGATE-ELEVATOR", then function match() returns ["NAVIGATE-ELEVATOR", "ELEVATOR"], and the ternary conditional ? will take value m[1] which is "ELEVATOR".

  • inRectangleArea(x0, y0,f x1, y1, frameId): Returns a boolean value indicating if the robot current pose lies within the rectangle determined by any two opposite corners (x0, y0) and (x1, y1). The function is only calculated if the robot reports a pose, and the given frameId matches. The last argument frameId is optional, and the coordinates would match the robot pose from any coordinate frame (this is not recommended, but it makes testing easier when only one frame is in use). This function can be used to define simple rules based on robots position in a map, such as no-go or exclusion zones.

Maps

There is a difference between real world coordinates and image coordinates on a screen. InOrbit represents the map coordinate frame using the right hand rule as the convention for coordinate axes and rotations. This takes inspiration from ROS and a more detailed explanation can be found in REP-103. Specifically, translation is X => forward/backward, Y => left/right, Z => up/down.

The 2D Map representations on InOrbit are a projection of the space looking down from above which means that in 2D, the direction in which the coordinates increase are X => right, Y => Up.

For images, the direction in which the coordinates increase are X => right, Y => down. (0,0) is in the upper left corner.

An image on your computer will most likely display the normal image coordinates system (Y increases as you move to the bottom). Maps on the InOrbit Platform are displayed using world-coordinates, so Y increases as you move to the top. The uploaded image is flipped vertically to accommodate this. Please make sure that the bottom left corner of the image matches the (metadata.x, metadata.y) coordinate of the real world.