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.
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.
On this tab you are able to create, rename and delete one or more API Keys.
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.
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.
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.
Check the APIs' documentation and specs here
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:
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:
Using the gray plus sign you can add new sections. Each section has a name and scope, that can be robot or fleet.
Using the gray plus sign inside the section you can add widgets from the widgets palette, as shown below:
Once you have defined the dashboard, you are ready to embed it into your applications.
To generate the embedding code, go to InOrbit Console > Robot Data tab > Dashboards section and use the “< >” button on the right, near the top.
Clicking this button shows a code fragment that can be used to embed the dashboard into any HTML page.
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:
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:
The following sections describe both SDKs in detail and provides examples of their usage.
The Robot SDK enables developers to implement bidirectional communication between robots and the cloud. It leverages the InOrbit Agent Core that implements the core communication mechanism in an efficient and secure way. Particularly the agent takes care of:
The Robot SDK provides various methods to ease communication between the agent and other software running on your robot, including:
The following diagram shows how the InOrbit agent interfaces with other software running on the robot:
Software on the robot can communicate with the agent using various mechanisms, depending on the chosen software stack.
How To Integrate With Custom Code - C++ example
#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;
}
How To Integrate With Custom Code - Python example
from inorbit.robot_sdk import LaserConfig
from inorbit.robot_sdk.robot import RobotSession
from math import inf
robot_session = RobotSession()
# Publish sample key-value pairs
robot_session.publish_key_values({"battery": 0.56})
# Publish a sample pose
robot_session.publish_pose({"x": 0.0, "y": 1.1, "yaw": 0.0})
# Set configurations parameters for a single lidar
laser_config = LaserConfig(
x=0,
y=0,
yaw=0,
angle=(-0.5, 0.5),
range=(2.0, 10),
n_points=10,
)
robot_session.register_lasers([laser_config])
# Publish readings for a lidar
lidar = [3.1, 3.3, inf, 5.3, 8.1, inf, 2.0, 5.5, 3.8]
robot_session.publish_lasers([lidar])
The Robot SDK integrates with both C++ and Python codebases. Available as a C++11 header-only library or a Python package it serves as a wrapper for communicating with the InOrbit Agent Core. The libraries can be downloaded from the Robot SDK for C++ or the Robot SDK for Python. Please note that the SDK is designed to work with the InOrbit Agent Core; refer to the Agent Core Installation section for installation instructions.
For more detailed examples, please refer to the corresponding Robot SDK repository.
To integrate from C++ or Python you must use the InOrbit Agent Core. 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:
Your account key is the last segment of the URL, A9wXdA5dsD5
in 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=core" | 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”.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.
#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.
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.
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.
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"
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>
The InOrbit agent includes support for ROS 2, using the publish/subscribe communication mechanism to exchange data with other software running on your robot.
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.
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, and as a Python package which is used in Python programs, such as connectors.
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.
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.
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.
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.
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.
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.
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.
A typical application of the Edge SDK is a program in the structure of a connector. A connector is a custom component that connects a third-party system to InOrbit. The open source Python package inorbit-connector provides a framework for building connectors using the Edge SDK. A simple example can be found in the package's GitHub repostory.
An example of a connector based on inorbit-connector
is the SICK Tag-LOC InOrbit Connector, which enables real-time location tracking of objects and vehicles.
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:
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.
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.
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.
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.
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.
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 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.
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:
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:
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
.
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.
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.
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.
# 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
.
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.# 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:
The apply
command can be used to create or update configuration objects. To apply a configuration object follow these steps:
Create a JSON file describing the configuration object. (The reference schemas for the different kinds are described in following sections)
Run inorbit apply -f config_file.json
. If everything goes well, you'll a message like
Configuration <config/file/path> applied successfully
.
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.
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.
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.
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:
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.
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.
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:
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'".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.
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:
app
slack#...
googleChat
opsgenie
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.
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 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:
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.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.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:
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()
--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.
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"
.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.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.
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.
Basic Robot Dashboard
{
"kind": "DashboardDefinition",
"apiVersion": "v0.1",
"metadata": {
"id": "my-robot-dashboard",
"scope": "account"
},
"spec": {
"label": "Robot",
"sections": [
{
"label": "Health",
"scope": "robot",
"withControlWidget": true,
"widgets": [{
"type": "chart",
"label": "Vitals",
"dataSources": [{
"id": "cpuLoadPercentage",
"label": "CPU",
"type": "gauge"
}, {
"id": "ramUsagePercentage",
"label": "Memory",
"type": "gauge"
}, {
"type": "chart",
"label": "Timeline",
"layout": {
"grid": 8,
"chroma": true,
"rows": 1
},
"dataSources": [{
"id": "cpuLoadPercentage",
"label": "CPU",
"op": "maximum"
}, {
"id": "ramUsagePercentage",
"label": "Memory",
"op": "average"
}]
}]
}
],
"order": 3
}
}
Dashboards define the user experience for you and other users in your account. They define what information is displayed to monitor and operate robot fleets, which is highly dependent on the robots characteristics, monitoring and operational needs, and size of fleet among other factors. Refer to this document for an overview of what you can do with Dashboards and its related concepts: Sections and Widgets.
This section gives a specification for dashboards to configure them through Configuration APIs. This is a rather advanced usage and allows to fine-tune the experience providing further control on each configuration parameter as well as to apply Configuration Management practices to your account. It is adviced to start with dashboards defined through the Settings interface, then export them using the Config APIs with the CLI, for an easier start with a mostly configured dashboard.
Dashboards can be customized depending on robot types or other conditions. Different widgets can appear to display only those that make sense to the selected robot, as well as to offer only the actions that apply to this robot. Further detail on Robot-based dashboards customization.
Dashboards are configured through the DashboardDefinition
kind, using a unique id
and only
at "account"
level. A dashboard must have a label
, and can define a numeric order
property
to define the order it appears as compared to order dashboards order
value.
Each dashboard defines a list of sections
. Sections also define a label
or title.
They use a withControlWidget
property to define if a control bar appears in the section,
allowing to select the current robot, or timeframe to display, or current filters.
it is normally recommended to have a control widget; except when multiple sections of the same
scope
appear in the same dashboard.
Sections also have a scope
that define the type of widgets that can appear in the section.
Valid scopes are:
"fleet"
: To display information about a set of robots (or the entire fleet), such as
an overview of all robots' statuses, a list of incidents, audit log, etc."robot"
: A section that displays (normally current) information about a selected robot"navigation"
: Normally contains a single Navigation widget, which allows to view a robot
in its map (position, planned path, costmap, traffic zones), with cameras and controls
to tele-operate it."timeCapsule"
: To contain Time Capsules,
a tool to analyze past events on a robot around a given point in time to perform root cause
analysis and optimize robot behavior."mission"
: Reserved for widgets to display missions' metrics (KPIs). Note that this feature
is not available on all Editions.Sections can also be displayed or hidden conditionally (see below).
Sections contain a list of widgets
, which are the actual contents and most important part
of a dashboard. There are many different widget types.
Every widget contains some common properties like a label
and a type
(see below), and a
layout
defining its size and position. Just like sections, widgets can also be displayed
conditionally (see below).
Depending on the widget type
, each widget accepts its own parameters in a config
object:
for example which data sources to display, or what actions to offer to execute, etc.
Valid options for config
objects are listed below; except for widgets that do not have
any configuration options (they only accept an empty {}
object).
"fleetStatus"
: The main widget to display the list of robots
and their statuses (with Error, Warning, Ok). It can be configured with a list statuses
, ids
of StatusDefinition
objects to display a specific list of statuses (by default it displays those
configured globally in Settings UI)."incidentTimeline"
: A calendar-like timeline of incidents for the entire fleet (or selected robots)."incidentList"
: A listing incidents for the entire fleet (or selected robots). It works in sync
with the Incident Timeline, and allows jumping to Time Capsule to dig into the time window of an incident."kpi"
: A widget with account-level KPIs such as number of robots, users/robots ratio, etc."locations"
: A world map with indicators of your configured locations and their states; that
can be zoomed in at location level to display its robots."auditLogFleet"
: An audit log for the fleet or a selected set of robots."fleetMissionTracker"
: A listing of missions already executed and being executed by the robots fleet.
It allows zooming into past missions to debug in Time Capsule.All robot widgets display information or act on a selected robot, which can be selected from the control widget or widgets that support selection (Fleet Status, Navigation).
Data Sources:
"chart"
: A timeline chart displaying variation in values of multiple data sources.
It displays a line chart by default, unless a chartType
property is used (valid values: "linechart"
and "areachart"
).
It requires a list dataSources
objects with the data sources to display. Each data source must have an id
and label
, and can also specify a numeric precision
and scale
to adjust values; and also the operation
to perform to aggregate values in time windows (one of "average"
, "count"
, "maximum"
, "minimum"
, "sum"
, "last"
).
Scale in Y-axis is automatically adjusted, unless two properties min
and max
are given to fix the scale.
See example on the right sidebar."vitals"
: A widget that supports displaying up to 4 data sources values.
Its dataSources
list contains the data sources identified with id
and label
.
An optional unit
can be specified for each data source, as well as a type
of visualization:
a "text"
representation of a "gauge"
used normally with numeric values."listData"
: It displays several data sources in a table. It is configured similarly to "vitals"
,
with a list of dataSources
with id
, label
and precision
."keyValues"
: It displays a list of all key-value pairs reported by the robot with their most recent updatd timestamp."history"
: It displays a table with the most recent updates received to a specific data source,
and their timestamps."stateTransitions"
: Similarly to chart
, it displays a timeline of several data sources;
with colored bars representing how long each data source stays in a given value and changing colors
when those states change. It is configured with a dataSources
list of objects with id
and label
.Monitoring and operation:
"localization"
: A small version of the Navigation widget (see next scope), displaying the robot
in a small map."actions"
: Displays configured actions that can be execute on the robot. All configured actions
are displayed, except if a list of strings actionIds
is explicitly given to limit the selection.
Two other boolean values can be used to tweak the appearance: expanded
will auto-expand action groups,
and bigButtons
displays larger buttons (useful for dashboards prepared for mobile devices)."cameraWidget"
: A widget displaying the a live camera. It is configured with a single cameraId
string that identifies the camera to display."tagsAndModes"
: A listing of all tags assigned to a robot, including its current Mode. It also
allows editing those tags."missionTracker"
: It displays the most recent missions executed by the selected robot.Auditing
* "auditLog"
: Displays a log of the most recent events of the robot (or those events in the selected
time window).
* "diagnostics"
: A visualization of data reported as ROS Diagnostics, for ROS-based robots.
* "dataBags"
: A listing of rosbags and other files (logs, etc.) configured to be collected by the robot.
Each file has an option to be uploaded from the robot to the cloud (to later download it, without the
user needing to connect to the robot).
"navigation"
: The only widget for a Navigation dashboard.
It displays a map with the current position of the robot in its map, displaying (if reported) its
paths, costmap, lidars readings and camera images.
It can also display other robots in the area, as well as map features
such as traffic zones, named waypoints, etc.
Additionally, if supported by the robot and configured, it also allows doing teleoperation.These widgets are specialized versions of some widgets already listed above; with the excpetion that they do not show "live" data but instead are controlled through the Time Capsule toolbar, to display past data at specific points in time.
"timeCapsuleMap"
: A version of "navigation"
to work within Time Capsule."timeCapsuleSegmentLogs'"
: A version of stateTransitions
to work within Time Capsule"timeCapsuleAuditLogs"
: A version of auditLog
to work within Time Capsule"timeCapsuleDataBags"
A version of dataBags
to work within Time Capsule"missionDefinitions"
: It displays mission definitions and their steps.
It also allows dispatching missions for their execution."missionKPI"
: It displays aggregated information on missions executed by the fleet, allowing
to perform various filters such as by time, robot tags, mission results.{
"kind": "DashboardDefinition",
"apiVersion": "v0.1",
"metadata": {
"id": "my-dashboard",
"scope": "account"
},
"spec": {
"sections": [{
"label": "A conditional section",
"conditional": {
"tags": ["only-this-tag"]
},
"widgets": [...]
}]
}
}
To allow conditional display of widgets or entire sections, both elements allow to be toggled on only if the current robot has been assigned a tag. This allows creating dashboards that will display differently for different robots. See Robot-based Customization section for more details.
The configuration for this feature is given by an object like { "conditional": { "tags": ["my-tag"] } }
,
specifying either in a section or a widget that will only be displayed if the robot contains tag "my-tag".
{
"kind": "Preferences",
"apiVersion": "v0.1",
"metadata": {
"id": "all",
"scope": "account"
},
"spec": {
"navigationWidget": {
"panels": {
"teleop": false
},
"actions": ["action1", "action2"]
}
}
}
Additionally to their configuration given in Dashboards, some parts of the user interface may
be adjusted using the Preferences
kind.
This also includes global properties that apply to all widgets if a given type.
And most importantly, Preferences
can be applied at different scopes ("account", "tag" or "robot")
so the preference will be applied differently depending on the selected robot.
Currently, only Navigation widget allows setting Preferences. They include:
panels
: An object with panel names that can be hidden. Its keys can be "teleop"
, "camera"
, "gauges"
, "localization"
and "background"
.
By setting any of these values to false
, the Navigation widget will not display them.
This is useful for example when a robot does not implement a feature or simply teleoperating it is not desired.actions
: A list of ids of actions to display in the Navigation toolbar, which allow
for quick execution of actions while monitoring the robot. Applying these Preferences
at the correct scopes, one could only offer actions that apply to a robot type, or robots
in a specific location for example.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 screenshortLabel
: Optional short label to replace the default label (normally the first letter of label
)rosTopic
: The ROS topicwidth
: Integer of width in pixelsheight
: Integer of height in pixelsrate
: The number of frames per secondoutputEncoding
: Encoding type, default is "rgb8"quality
: Integer of quality between 0 and 100rotation
: Rotation of the camera feed in multiples of 90mirror
: Optional boolean for mirroring image. Default is falseenabled
: Boolean for enabling camera. Default is trueRobot Footprint Object Example
{
"metadata": {
"scope": "<scope>",
"id": "all"
},
"apiVersion": "v0.1",
"kind": "RobotFootprint",
"spec": {
"footprint": [
{ "x": -0.5, "y": -0.5 },
{ "x": 0.3, "y": -0.5 },
{ "x": 0.7, "y": 0.0 },
{ "x": 0.3, "y": 0.5 },
{ "x": -0.5, "y": 0.5 },
],
"radius": 0.2,
}
}
The RobotFootprint
kind allows configuring the footprint of a robot. The footprint is the area that the robot occupies in a map and it is used for representing the robot in map views.
Unlike most other configurations in this section, RobotFootprint
is a "global" config, meaning there can only be one per unique scope. For this reason, the only accepted id
value is "all"
.
The spec
of a robot footprint configuration has the following properties:
footprint
: (Optional) Footprint shape, defined as a list of points of minimum length 3. Each point is an object with x
and y
properties, representing the position of the point in meters relative to the reported position of the robot. If not provided, the footprint will take a circular shape with a radius set by the radius
property.radius
: (Optional) Sets the size of the inner orientation indicator. If set to 0
, no indicator will be shown. If footprint
is not provided, the radius of the circular footprint will be adjusted proportionally to this number.Note: If only one of the two properties is provided, the other will be taken from the configuration applied to the robot's tag(s) or the configuration at the account level.
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.
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.
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 successfulMission 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.
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.Managing robots fleets, in particular with multiple and heterogeneous robots in a same location require effective orchestration of their work. One aspect of this orchestration is controlling where and how robots can navigate in their location, and if they should follow any traffic rules. InOrbit supports controlling traffic by defining Zones in a map, assign them traffic rules or implement your own custom logic and representation for them.
Note: This feature is only available to some Editions.
Traffic Zone Type config
apiVersion: v0.1
kind: TrafficZoneType
metadata:
scope: account/<accountId>
id: charging
spec:
label: Charging Zone
# Only one robot is allowed to enter this zone at a time, so we use "soz" (Single-Occupancy Zone)
# traffic rule
rules:
- soz
- type: nogo
options:
priorityTags:
tagId: 10
ignoreTags:
- camera
# Color for these zones will be green, except when state value is "occupied"
style:
color: "#66c2a5"
conditionalStyle:
occupied:
color: "#e5c494"
events:
# Whenever a robot enters the zone, we want to execute an action on it. In this example,
# turning on ambient lighting on the charging zone (identified by room001)
- type: action
actionId: turnOnLights
targetId: room001
event: onEnter
The TrafficZoneType
kind is used to define the type of zones requiring traffic attention.
For example: parking or docking areas reserved for robots, areas with special traffic rules
such as a narrow alley where only one robot should be allowed at a time, or areas where robots
are not permitted to enter.
Zone Types can be assigned traffic rules
, to enforce such behaviors. Currently,
only "nogo"
and "soz"
(for Single-Occupancy Zone)
rules are implemented out of the box.
Contact support or your customer engineer for help to implement custom traffic rules.
By default, two TrafficZoneType
s are configured ("nogo"
and "soz"
)
in all accounts; with these two rules for No-Go zones
and Single-Occupancy. Any number of user-defined zones can be configured.
All zone types can have different styles (color, transparency) to be visually distinguished
in the navigation maps.
They can also use different styles according to their internal state. In the example, a soz
zone
defines a different color to use when its state has value "occupied"
, as defined in the
conditionalStyle
section. The state is changed by traffic controllers ("free", "occupied")
and it can also be modified using
REST APIs.
TrafficZoneType
s are configured for "account" scope, ie. globally for all robots and locations.
Events. Zones can have events defined, that trigger when robots enter or leave the zone, when
the zone first become occupied by any robot, or when the last robot leaves. These are defined in
the events
section.
The event
value must be one of "onEnter"
, "onExit"
, "onEmpty"
or "onOccupied"
; and it
triggers a robot action
(the only event type supported initially), which can be run on the robot that
initiated the event or in a different robot or device by using the targetId
. The action actionId
must be configured in that robot.
For example, one might want a robot to slow down when entering a zone, by running a specific action
that configures its speed; or turn on/off ambient lighting while the robot is in the zone
for a vision-based robot navigation system to work properly.
Traffic Zone config
apiVersion: v0.1
kind: TrafficZone
metadata:
scope: tag/<accountId>/<locationId>
id: chargingZone001
spec:
label: South-West Charger
type: charging
frameId: map
geometry:
# Polygon coordinates are normally obtained when drawing the zone in the app
polygon:
- x: 0
y: 0
- x: 1
y: 0
- x: 1
y: 1
- x: 0
y: 1
bufferDistance: 0.5 # (Optional) Defines a buffer distance for the zone
events:
# Individual zones also support events. This example would run "beepbeep" action on the
# robot entering the zone
- type: action
actionId: beepbeep
event: onEnter
The TrafficZone
kind represents each of the zones defined in a location map.
They contain the actual coordinates of a polygon representing their two-dimensional area:
each of the elements of list polygon
must be a point given as x
and y
coordinates.
Note that to assist in defining zones, it is advised to use the editor in Navigation map,
enabled by clicking Layers > Zones.
Zones are defined for physical spaces, which are identified by both the locationId
(a tag)
and the frameId
the robot is using in that location.
Each zone has a unique type
, which must be one of the configured TrafficZoneType
s.
A zone will be represented in the map using the style given by its zone type.
It will also use any traffic rules defined in the type.
Note that zones can be useful even if their type has no traffic rules assigned to them, in multiple ways:
isRobotInZone('dropZone') and not isZoneOccupied('armReachArea')"
Zones can also be assigned events
invidually. Events are described in the TrafficZoneType
section. When any zone event occurs, both the triggers defined in the TrafficZone
and those
in its TrafficZoneType
are executed.
If the user wants to detect or stop robots before they enter the defined zone geometry, the zone can be configured
to emit onEnter
events when the robot approaches but has not yet entered the zone.
The property bufferDistance
can define an area around the defined polygon so that onEnter
events trigger
when a robot (or part of it) is less than bufferDistance meters from the actual zone polygon.
InOrbit gives robot end-users and also robot developers the ability to mix and match robots from different vendors and operate them through a single platform.
Monitoring and operating diverse types of robots effectively requires focusing on each robot’s characteristics: some may use different status and incident configurations; some will have configured different sets of actions for operators or different data sources and cameras to observe. This also applies to robot models from the same maker; with added sensors to monitor or capabilities that reflect in operator actions.
InOrbit offers different approaches to customize the app’s user interface so that it presents the relevant information according to each robot type – or to the right situation.
Most elements presented in the user interface are customizable through Dashboards’ configuration Enter Settings > Robot Data > Dashboards, and customize your dashboards to your preference by adding sections or widgets with the information relevant to your scenario.
In order to customize further details not always available in the Settings screens, and also for better control of configurations as your scenario and complexity of configuration grows over time, we highly recommend using Configuration as Code through are Config APIs.
Using the DashboardDefinition kind you can define dashboards to suit your exact needs. It is recommended to start through the Settings interface for an initial version, then export its configuration. Using the InOrbit CLI: inorbit get config –kind DashboardDefinition –yaml > my_dashboards.yaml
and continue by tweaking parameters in that file, applying the changes every time: inorbit apply -f my_dashboards.yaml
.
It is also recommended to use version control (such as a Git repository) to control your configuration versions as well.
In the next sections, we assume a basic understanding of Config API, the InOrbit CLI and an initial working version of dashboards with a pre-created YAML file.
Conditional widget example
apiVersion: v0.1
kind: DashboardDefinition
metadata:
id: <DASHBOARD-ID>
scope: account/<ACCOUNT-ID>
spec:
label: Robot
sections:
- label: Hardware
scope: robot
widgets:
- label: Health
type: vitals
config:
dataSources: ["cpu", "memory", "battery1-volt", "battery1-temp"]
- label: Dual Battery
type: vitals
conditional:
tags:
- dualBattery
config:
dataSources: ["battery2-volt", "battery2-temp"]
Dashboards can be configured with variants to display the most relevant information and the appropriate actions for each robot type – or robot situation. In some cases, widgets only make sense to a specific robot configuration or state.
Dashboards sections and widgets can be configured to be displayed only when the selected robots has a specific tag. As tags can be used to represent robot types or hardware models, versions, customers, locations, modes, etc., this approach allows adapting dashboards to most "conditional" situations.
In this simplified example, creating a dashboard with one Robot section. (To create a more realistic dashboard, it is recommended to export an existing dashboard configuration and fine-tune it using code as displayed here). This section has two widgets. The first ("Health") is displayed for all robots. The second widget ("Dual Battery") only appears when the robot has a tag "dualBattery", and displays some data sources indicating battery temperature and voltage.
The same conditional
property shown here in widgets can be applied to sections, making visibility of an entire section conditional to robot properties.
Tag-based configuration tweaks for Navigation widget
apiVersion: v0.1
kind: Preferences
metadata:
id: all
scope: tag/<ACCOUNT-ID>/<ROBOT-TAG>
spec:
navigationWidget:
actions:
- cancel-navigation-action-id
panels:
teleop: false
The Navigation dashboard is core to many robots operations, allowing it to monitor the robot position within a map, its surroundings as detected by sensors (e.g. lidars), planned navigation, cameras, etc. It also allows operating the robot: Sending the robot to a waypoint, tele-operating it to adjust its position, relocalize it, execute arbitrary actions, etc.
Some of that functionality may not apply to some robots. In this case, it is preferable to hide some elements from the display to un-clutter the user interface, as well as to simplify operators' work.
The elements presented for navigation are set using Preferences Config API kind, and apply to all Navigation dashboards. These options: * The list of actions to offer the operator to execute (displayed on the toolbar). These must be pre-configured actions (Settings > Insights > Actions, or ActionDefinition kind in Config API). * Displaying (or not) certain panels on screen. For example, omit cameras if the robot does not have one, or the teleo-operation controls if they do not apply to a robot type (devices fixed to the ground, or large machines where manual teleoperation is disallowed).
The simple example in the sidebar demonstrates how to hide the teleop controls and offer quick access to execute a specific action.
Note that this Preference configuration is applied only for a specific tag, so it only applies to robots with that tag. Combining multiple of these configurations, the Navigation view can be customized to different types of robots.
For a full reference on dashboards, sections and widgets, check Dashboards Config API.
Time Capsule configuration example
apiVersion: v0.1
kind: DashboardDefinition
metadata:
id: <DASHBOARD-ID>
scope: account/<ACCOUNT-ID>
spec:
label: Time Capsule
sections:
- label: Time Capsule
scope: timeCapsule
conditional:
tags:
- cleanerRobots
withControlWidget: true
widgets:
- label: Map
type: timeCapsuleMap
config: {}
layout: { "chroma": true, "grid": 8, "height": 2 }
- type: group
label: ""
layout: { "grid": 8, "height": 2 }
widgets:
- config: {}
label: Robot Log
layout: { "chroma": true, "grid": 12, "height": "300px" }
type: timeCapsuleAuditLogs
- type: chart
label: Timeline
config:
chartType: linechart
dataSources:
- id: cpuLoadPercentage
label: CPU
- id: ramUsagePercentage
label: RAM
layout: { "chroma": true, "grid": 12, "height": "120px" }
- type: chart
label: Sensors
config:
chartType: linechart
dataSources:
- id: cpuLoadPercentage
label: CPU
- id: ramUsagePercentage
label: RAM
layout: { "chroma": true, "grid": 12, "height": "120px" }
InOrbit’s Time Capsule is a powerful tool to analyze past events to perform Root Cause Analysis on incidents, analyze missions performance, etc. As debugging may focus on internal robots’ details, it may require fine tuning specific to robot types. Creating different conditional Time Capsules is very useful to customize the experience based on the robot type; see example below.
Additionally, assembling the ideal Time Capsule configuration may require multiple widgets and a complex layout, to all work in sync (displaying the same past timestamp to rewind through an event). The Settings interface offers to select through a few layout presets, but the Configuration API allows for fine tuning of these layouts. The code below also presents a (complex) layout not available in the defaults, as an idea for customizing your dashboards.
Note that this rather simplified (yet long) example is conditional and would display the entire Time Capsule section to only a set of robot types (tagged "cleanerRobots"). It also makes use of a group, to stack some widgets to the right of a large map, and fine-tunes their individual layout to adjust the height of each widget: using numbers to represent "rows", or quoted strings for exact heights in pixels. Layout properties (width, height) are inspired by the classic 12-column web layout system. Note that we included the layouts using JSON syntax simply to use less code lines (JSON is part of YAML format).
The Fleet Status Widget is the main access point to your robots fleet and can display a combination of high-level information on the fleet status as well as detailed robots statuses allowing to drill down on specific information.
Robots will normally have several statuses defined (as for example battery level, ROS status, temperature sensors) which may be at "OK", "Warning" or "Error" levels. Some statuses (as well as their associated data sources) may make sense to parts of the fleet and not others. Normally, when the fleet combines different robot types, there will exist different StatusDefinition configurations defined at tag levels, to apply to different sub-fleets. When this happens, the Fleet View widget will adapt its appearance to display detailed or summarized information according to whether the fleet (or groups) in display is "heterogeneous", ie. if the robots status configurations are different across a displayed group of robots. This is another form of customizing the displayed information, adapting to robots’ configurations.
We reviewed different ways to customize the InOrbit user interface to present the most relevant information for each robot, which may change according to the robot type, configuration or its current state. Access to some of these advanced features is given through the Config APIs, which can also bring advantages from doing Configuration Management on your configuration files.
For more information, questions and feedback contact support@inorbit.ai.
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
orBELOW
rules in aStatusDefinition
.
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.
false
and true
and null
.+
, -
, *
(multiplication), /
(division), %
(remainder), ^
(exponentiation) as well as not
, and
, or
(boolean operators).==
, !==
, >=
, <=
, >
and <
(equals, not equals, etc.)(
and )
to make precedence explicit.[]
, e.g. a[3]
. They can be concatenated with ||
.minValue("battery", 30)
.=
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:
(...)
: groupingf()
, 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 ANDor
: Logical ORx ? y : z
: Ternary conditional: if x
then y
else z
.=
: Variable assignment;
: Expression separatorThe 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:
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
).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.
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.
When using Traffic Management,
expressions can use functions to query about the state of TrafficZone
s.
isRobotInZone(<zoneId>, [<locationId>])
: Determines if the current robot (where the
expression is being evaluated) is currently inside the zone zoneId
. This identifier is
normally sufficient to identify the zone, which must be defined in the robot's current
location. Optionally the locationId
(a tag) can also be provided.
As some expressions (e.g. in Mission Definitions or Zone Events) can be evaluated in arbitrary
robots, this function can also be used to ask if any robot is currently inside a
configured zone.
isZoneOccupied(<zoneId>, [<locationId>])
: Tells if there is currently at least one robot
in a given zone. The zone is identified in the same way as in isRobotInZone
.
For example, a mission step might waitUntil
a zone becomes empty (for example to drop
some cargo or to move on a narrow area).
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.