4. Developer Platform Usage
π Duration: 1-2 hours
πͺ Level: Advanced
Tutorial Overviewβ
Here we cover advanced usage of MoveIt Pro as a developer platform and a production system. MoveIt Pro is built on ROS 2 and uses Docker to encapsulate its runtime. As a developer, you will interact with MoveIt Pro through various command-line tools, creating custom configuration packages, and building unique behavior plugins. By the end of this tutorial, you should understand:
- The MoveIt Pro command-line interface and its verbs for building, configuring, running the system, and accessing tools like RViz and logs.
- How to create and customize MoveIt Pro configuration packages for your own robot (including inheriting from existing configurations).
- How to develop custom Behaviors (plugins) using MoveIt Pro
- How to integrate external ROS 2 interfaces (like topics, services, and actions) into MoveIt Pro behaviors.
- How to use the MoveIt Pro SDK Python interface and standard ROS 2 CLI tools to programmatically run Objectives (behavior trees) without the GUI.
Pre-reqsβ
You should have already installed MoveIt Pro. We will assume you have already completed Tutorial 3 and know how to build Behavior trees.
Command-Line Usageβ
MoveIt Pro provides a command-line tool called moveit_pro
with multiple subcommands (or βverbsβ) to manage the development and runtime environment.
Below is an overview of key moveit_pro commands and how to use them.
moveit_pro configureβ
Use moveit_pro configure to set up or update the configuration of MoveIt Pro on your system. This command is interactive and will prompt you for:
- User workspace path: The path to your MoveIt Pro user workspace (where your ROS 2 packages and robot configuration reside).
- Configuration package name: The name of the MoveIt Pro configuration package in your workspace to launch (e.g. lab_sim).
- DDS configuration override: MoveIt Pro can optionally use a custom DDS configuration. Choose βNoβ at this prompt to use MoveIt Proβs default DDS settings (recommended). If you wish to use a custom Cyclone DDS configuration, you can configure your host system and select "Yes" to have MoveIt Pro configure thet Docker containers to match the host DDS.
- License key: Your MoveIt Pro license key (you should have received this via email).
moveit_pro buildβ
The moveit_pro build command compiles and builds all parts of the MoveIt Pro system. The steps to do so include building a Docker overlay, and compiling the workspace. Common usages include:
- moveit_pro build: Build everything (Docker images and your workspace code).
- moveit_pro build user_image: Build only the MoveIt Pro Docker overlay images. MoveIt Pro uses three main Docker images (named agent_bridge, drivers, and frontend), which are built from the Dockerfile in your user workspace and share a common base image. Use this when you have modified the Dockerfile or added new system dependencies (e.g. via package.xml dependencies) that require rebuilding the containers.
- moveit_pro build user_workspace: Build only your ROS workspace packages (your robotβs configuration package, additional ROS packages and source code dependencies, and any custom behaviors). This is fastest if you've only changed your code.
You can pass additional colcon arguments to skip or select certain packages using:
moveit_pro build user_workspace --colcon-args "--packages-select my_package"
moveit_pro runβ
After configuring, use moveit_pro run to launch MoveIt Pro with your chosen configuration. You can add additional launch-time arguments as follows:
-w, --user-workspace <PATH>
: Specify a user workspace path to use (overrides the configured default).-c, --config-package <NAME>
: Specify the MoveIt Pro configuration package to launch.-n, --no-drivers
: Do not launch any robot driver containers.-o, --only-drivers
: Only launch the robot driver containers.-h, --headless
: Run in headless mode (no GUI front-end).-v, --verbose
: Enable verbose output for debugging.--no-browser
: Prevent automatically opening the web UI in your browser.
For example, to run MoveIt Pro in verbose mode:
moveit_pro run -v
moveit_pro rvizβ
You can use you own RViz configuration and run it on your host system or inside a MoveIt Pro image. For an RViz configuration that we recommend, you can load our sample using:
moveit_pro rviz
The RViz window will help you visualize:
- The planning scene and any collision objects added to it
- The TF tree of the robot
- The MoveIt Motion Planning Task plugin
- This plugin allows you to visualize partial paths and sub-optimal solutions
- Use this tool for deeper introspection in to the motion planning piepline, particularly with MoveIt Task Constructor (MTC).
- Pointcloud and camera topics
moveit_pro logsβ
Open the ROS 2 logging interface (rqt_console) to inspect log messages:
moveit_pro logs
Other Useful moveit_pro Commandsβ
- moveit_pro down β Stops and shuts down MoveIt Pro and all services.
- moveit_pro test β Build and run colcon tests for in user workspace.
- moveit_pro shell β Open a shell inside the MoveIt Pro runtime Docker container.
MoveIt Pro Workspaceβ
In MoveIt Pro, all custom configurations, Behaviors, and external code are represented as
ROS 2 packages in a workspace.
This is where the moveit_pro
launcher will look to build and run your robot workspace.
Below is an example of a typical MoveIt Pro workspace:
moveit_pro_example_ws/
βββ Dockerfile
βββ docker-compose.yaml
βββ colcon-defaults.yaml
βββ src/
βββ lab_sim/
β βββ config/
β βββ launch/
β βββ objectives/
β βββ src/
β βββ package.xml
β βββ CMakeLists.txt
βββ additional_configuration_packages/
βββ external_dependencies/
PickNik provides an empty workspace called moveit_pro_empty_ws, which you can fork and add your own configuration package to. If there are changes or additions to our recommended configuration, your fork will ask to sync the upstream changes.
Workspaces for Production Robotsβ
In production systems, it is often desirable to have a customized Docker build and bring-up procedure specific to that hardware. The MoveIt Pro Runtime is distributed through Docker base images that are built into production images. When the robot starts, Docker services will launch the MoveIt Pro runtime, drivers, developer platform user interface, and/or additional Docker containers using your production image. A production image is built using the base MoveIt Pro image, your ROS 2 workspace, and MoveIt Pro configuration packages according to the steps in your Dockerfile.
The following diagram illustrates how a production system would share and build packages in the MoveIt Pro runtime:
The ROS 2 workspace can be built into the image as part of the Docker build context, mounted at runtime, or some combination of both. See our Docker Containerization page for more detail.
MoveIt Pro Configuration Packagesβ
A MoveIt Pro configuration package is a ROS 2 package that describes your robot and its capabilities.
It defines:
Your robot
- Robot and environment (URDF) models
- MoveIt configuration: motion planners, inverse kinematics solvers, allowable collisions, etc.
- Controller and camera configuration
What your robot can do
- Objectives and waypoints
- Launch files: To start drivers/simulators, additional ROS nodes/processes, etc.
- Custom behaviors (C++ source files)
Creating a New Robot Configurationβ
If you are onboarding a new robot to MoveIt, the MoveIt Setup Assistant can help you partly configure your robot. For xacro troubleshooting tips see here.
Inherited Configuration Packagesβ
Package inherticance is a powerful tool that allows you to create base packages that have everything generic to your robot, then use inherited packages to add or override certian aspects.
Some example use cases for inherited packages:
- Minor hardware differences
- A configuration package for a specific robot that either lacks a gripper, or the robot has multiple end-effectors to choose from
- IP addresses, kinematic calibration, or other unique details
- Additional Objectives, Behaviors, or sensors
- Mobile base and navigation behaviors
- Perception and ML behaviors
- Stereo camera and object registration
Hands On: Derived Package with Minimum Requirementsβ
The follow steps will walk you through creating a MoveIt Pro configuration package that is based on an existing package (in this case, lab_sim
):
We will assume your workspace is located at the standard ~/moveit_pro/moveit_pro_example_ws
location. You can modify the below paths for any location, however.
-
First we begin with a
config.yaml
. Lets create it now:mkdir -p ~/moveit_pro/moveit_pro_example_ws/src/example_config/config
touch -p ~/moveit_pro/moveit_pro_example_ws/src/example_config/config/config.yaml -
In the editor of your choice, add the following to
config.yaml
:based_on_package: lab_sim
-
Navigate to the top level of your package
~/moveit_pro/moveit_pro_example_ws/src/example_config
. -
Add three files:
CMakeLists.txt
,package.xml
, andlaunch/agent_bridge.launch.xml
:CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(example_config)
find_package(ament_cmake REQUIRED)
# Add waypoints, objectives, and other folders here if you want to override them
install(DIRECTORY config launch DESTINATION share/${PROJECT_NAME})
ament_package()package.xml
<?xml version="1.0"?>
<package format="3">
<name>example_config</name>
<version>0.1.0</version>
<description>Example derived MoveIt Pro config package</description>
<maintainer email="support@picknik.ai">MoveIt Pro Maintainer</maintainer>
<license>BSD-3-Clause</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<exec_depend>lab_sim</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>launch/agent_bridge.launch.xml
<?xml version="1.0" encoding="UTF-8" ?>
<launch>
<include file="$(find-pkg-share moveit_studio_agent)/launch/studio_agent_bridge.launch.xml"/>
</launch>
After creating your new configuration package, you can rebuild and launch using:
moveit_pro build user_workspace
moveit_pro run -c example_config
Next stepsβ
Try overriding various parts of the lab_sim
configuration, like the Objective folder location, loaded behaviors, or robot description.
Click to for an example
Here is how to override the Objectives folder
- Add this to your
config.yaml
:objectives:
objective_library_paths:
example_objective:
package_name: "example_config"
relative_path: "objectives" - Update your
CMakeLists.txt
:install(DIRECTORY config launch objectives DESTINATION share/${PROJECT_NAME})
- Make sure colcon builds your
objectives
folder (it may not get symlinked to your install folder if it is empty)!
Developing Custom Behavior Pluginsβ
Next we will build some example behaviors out of the AsyncBehavior
class.
In the Creating Custom Behaviors how to guide, more details for creating MoveIt Pro behaviors are covered.
Using the graphical interface or command line, create a new AsyncBehavior
.
Open your new behavior's .cpp
file and modify its doWork()
method:
tl::expected<bool,std::string> MyBehavior::doWork() {
auto ports = moveit_studio::behaviors::getRequiredInputs(getInput<int>("int_1"),
getInput<int>("int_2"));
if (!ports.has_value()) {
return tl::make_unexpected("Missing inputs: "+ports.error());
}
const auto& [a,b] = ports.value();
setOutput<int>("int_out", a + b);
return { true };
}
Add input and output ports to the behavior:
BT::PortsList MyBehavior::providedPorts(){
return {
BT::InputPort<int>("int_1",0,"The first number"),
BT::InputPort<int>("int_2",0,"The second number"),
BT::OutputPort<int>("int_out","The result")
};
}
Now, when we run our behavior, it will add two blackboard integers for us.
Try out blackboard introspection to see your result
Adding Multiple Behavior Plugins in a Single Packageβ
By default, each behavior gets put into its own ROS package, but it is often more convenient to have a collection of associated behavior plugins in the same ROS package.
To add additional behaviors to your package, we must create the relevant .cpp
and .hpp
files and register the behavior in register_behaviors.cpp
.
Lets create a new behavior to add two blackboard values using an external ROS service:
Click here for service_client.hpp
#pragma once
#include <example_interfaces/srv/add_two_ints.hpp>
#include <moveit_studio_behavior_interface/service_client_behavior_base.hpp>
using moveit_studio::behaviors::BehaviorContext;
using moveit_studio::behaviors::ServiceClientBehaviorBase;
using AddTwoInts = example_interfaces::srv::AddTwoInts;
namespace my_behavior
{
class MyAddTwoIntsServiceClient final : public ServiceClientBehaviorBase<AddTwoInts>
{
public:
MyAddTwoIntsServiceClient(const std::string& name, const BT::NodeConfiguration& config,
const std::shared_ptr<BehaviorContext>& shared_resources);
/** @brief Implementation of the required providedPorts() function for the hello_world Behavior. */
static BT::PortsList providedPorts();
/**
* @brief Implementation of the metadata() function for displaying metadata, such as Behavior description and
* subcategory, in the MoveIt Studio Developer Tool.
* @return A BT::KeyValueVector containing the Behavior metadata.
*/
static BT::KeyValueVector metadata();
private:
/** @brief User-provided function to get the name of the service when initializing the service client. */
tl::expected<std::string, std::string> getServiceName() override;
/**
* @brief User-provided function to create the service request.
* @return Returns a service request message. If not successful, returns an error message. Note that the criteria for
* success or failure is defined by the user's implementation of this function.
*/
tl::expected<AddTwoInts::Request, std::string> createRequest() override;
/** @brief Optional user-provided function to process the service response after the service has finished. */
tl::expected<bool, std::string> processResponse(const AddTwoInts::Response& response) override;
/** @brief Classes derived from AsyncBehaviorBase must implement getFuture() so that it returns a shared_future class member */
std::shared_future<tl::expected<bool, std::string>>& getFuture() override
{
return future_;
}
/** @brief Classes derived from AsyncBehaviorBase must have this shared_future as a class member */
std::shared_future<tl::expected<bool, std::string>> future_;
};
} // namespace my_behavior
Click here for service_client.cpp
#include <my_behavior/service_client.hpp>
// Include the template implementation for GetMessageFromTopicBehaviorBase<T>.
#include <moveit_studio_behavior_interface/impl/service_client_behavior_base_impl.hpp>
namespace my_behavior
{
MyAddTwoIntsServiceClient::MyAddTwoIntsServiceClient(
const std::string& name, const BT::NodeConfiguration& config,
const std::shared_ptr<moveit_studio::behaviors::BehaviorContext>& shared_resources)
: ServiceClientBehaviorBase<example_interfaces::srv::AddTwoInts>(name, config, shared_resources)
{
}
BT::PortsList MyAddTwoIntsServiceClient::providedPorts()
{
// This node has three input ports and one output port
return BT::PortsList({
BT::InputPort<std::string>("service_name", "/add_two_ints", "The name of the service to call."),
BT::InputPort<int>("addend1", "The first int to add to the other."),
BT::InputPort<int>("addend2", "The second int to add to the other."),
BT::OutputPort<int>("result", "{result}", "Result of the AddTwoInts service."),
});
}
BT::KeyValueVector MyAddTwoIntsServiceClient::metadata()
{
return { { "subcategory", "Example Behaviors" },
{ "description", "Calls a service to add two integers and makes the result available on an output port." } };
}
tl::expected<std::string, std::string> MyAddTwoIntsServiceClient::getServiceName()
{
const auto service_name = getInput<std::string>("service_name");
if (const auto error = moveit_studio::behaviors::maybe_error(service_name))
{
return tl::make_unexpected("Failed to get required value from input data port: " + error.value());
}
return service_name.value();
}
tl::expected<AddTwoInts::Request, std::string> MyAddTwoIntsServiceClient::createRequest()
{
const auto a = getInput<int>("addend1");
const auto b = getInput<int>("addend2");
if (const auto error = moveit_studio::behaviors::maybe_error(a, b))
{
return tl::make_unexpected("Failed to get required value from input data port: " + error.value());
}
return example_interfaces::build<AddTwoInts::Request>().a(a.value()).b(b.value());
}
tl::expected<bool, std::string> MyAddTwoIntsServiceClient::processResponse(const AddTwoInts::Response& response)
{
setOutput<int>("result", response.sum);
return { true };
}
} // namespace my_behavior
To publish a toast message with your result, add the following to your processResponse()
method:
shared_resources_->logger->publishInfoMessage("Sum:", std::to_string(response.sum));
Register behavior in register_behaviors.cpp
:
moveit_studio::behaviors::registerBehavior<MyAddTwoIntsServiceClient>(factory, "MyServiceClient", shared_resources);
Click for the full register_behaviors.cpp
#include <behaviortree_cpp/bt_factory.h>
#include <moveit_studio_behavior_interface/behavior_context.hpp>
#include <moveit_studio_behavior_interface/shared_resources_node_loader.hpp>
#include <my_behavior/my_behavior.hpp>
#include <my_behavior/service_client.hpp>
#include <pluginlib/class_list_macros.hpp>
namespace my_behavior
{
class MyBehaviorBehaviorsLoader : public moveit_studio::behaviors::SharedResourcesNodeLoaderBase
{
public:
void registerBehaviors(BT::BehaviorTreeFactory& factory,
[[maybe_unused]] const std::shared_ptr<moveit_studio::behaviors::BehaviorContext>& shared_resources) override
{
moveit_studio::behaviors::registerBehavior<MyBehavior>(factory, "MyBehavior", shared_resources);
moveit_studio::behaviors::registerBehavior<MyAddTwoIntsServiceClient>(factory, "MyServiceClient", shared_resources);
}
};
} // namespace my_behavior
PLUGINLIB_EXPORT_CLASS(my_behavior::MyBehaviorBehaviorsLoader,
moveit_studio::behaviors::SharedResourcesNodeLoaderBase);
Update CMakeLists.txt
to build your new behavior:
set(THIS_PACKAGE_INCLUDE_DEPENDS moveit_studio_behavior_interface pluginlib example_interfaces)
add_library(
my_behavior
SHARED
src/my_behavior.cpp
src/service_client.cpp
src/register_behaviors.cpp)
Click for the full CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(my_behavior CXX)
find_package(moveit_studio_common REQUIRED)
moveit_studio_package()
set(THIS_PACKAGE_INCLUDE_DEPENDS moveit_studio_behavior_interface pluginlib example_interfaces)
foreach(package IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
find_package(${package} REQUIRED)
endforeach()
add_library(
my_behavior
SHARED
src/my_behavior.cpp
src/service_client.cpp
src/register_behaviors.cpp)
target_include_directories(
my_behavior
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
ament_target_dependencies(my_behavior
${THIS_PACKAGE_INCLUDE_DEPENDS})
# Install Libraries
install(
TARGETS my_behavior
EXPORT my_behaviorTargets
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES
DESTINATION include)
if(BUILD_TESTING)
moveit_pro_behavior_test(my_behavior)
endif()
# Export the behavior plugins defined in this package so they are available to
# plugin loaders that load the behavior base class library from the
# moveit_studio_behavior package.
pluginlib_export_plugin_description_file(
moveit_studio_behavior_interface my_behavior_plugin_description.xml)
ament_export_targets(my_behaviorTargets HAS_LIBRARY_TARGET)
ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS})
ament_package()
Update package.xml
with our new dependency:
<depend>example_interfaces</depend>
Click for the full package.xml
<?xml version="1.0" encoding="utf-8" ?>
<package format="3">
<name>my_behavior</name>
<version>0.0.0</version>
<description>My description</description>
<maintainer email="support@picknik.ai">
MoveIt Pro User
</maintainer>
<author email="support@picknik.ai">
MoveIt Pro User
</author>
<license>TODO</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<build_depend>moveit_studio_common</build_depend>
<depend>moveit_studio_behavior_interface</depend>
<depend>example_interfaces</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_cmake_gtest</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
<buildtool_depend>python3-colcon-common-extensions</buildtool_depend>
</package>
Communicating With External ROS Interfacesβ
Check out the Behavior Specialization section for more examples of specialized behaviors to communicate with ROS interfaces like topics, services, and actions.
Next we will test out our previously built new behavior. You will need to install an example service server using the following command: Make sure MoveIt Pro is running first!
sudo apt update && sudo apt install ros-humble-examples-rclcpp-minimal-service
ros2 run examples_rclcpp_minimal_service service_main
The above steps only install packages in the running instance of MoveIt Pro.
When you end the session (moveit_pro down
) the changes will not persist.
To make them permanent, install them in your production image by adding this to your Dockerfile:
Click to expand
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=bind,target=${USER_WS}/,source=. \
. /opt/overlay_ws/install/setup.sh && \
apt-get update && \
rosdep install -q -y \
--from-paths src \
--ignore-src && \
apt-get install -y --no-install-recommends \
ros-humble-examples-rclcpp-minimal-service ros-humble-ros2action
Programmatic SDK Controlβ
MoveIt Pro can run as a standalone application, or be integrated into your own primary application, as just a runtime. While the MoveIt Pro UI can be used to build, edit, and execute Objectives it is not required to use it. MoveIt Pro Objectives can be launched and communicated from external processes through ROS interfaces.
Using the MoveIt Pro SDK to Start Objectivesβ
In the previous section, we wrote behaviors that can call services when ticked. In this section, we will use ROS services to start Objectives.
First we need to add the MoveIt Pro SDK repository as a git submodule, then we will build our production image:
cd ~/moveit_pro/moveit_pro_example_ws/src/external_dependencies
git submodule add https://github.com/PickNikRobotics/moveit_studio_sdk.git
moveit_pro build
Now we can run MoveIt Pro, and from a separate shell we can start an Objective using a ROS service:
moveit_pro shell
ros2 service call /execute_objective moveit_studio_sdk_msgs/srv/ExecuteObjective "{objective_name: 'Test My Objective'}"
If you have not created an Objective called Test My Objective
, the service request will fail.
If you need to cancel a running Objective, like the non-teminating 3 Waypoints Pick and Place
Objective use:
ros2 service call /cancel_objective moveit_studio_sdk_msgs/srv/CancelObjective
Using the MoveIt Pro SDK to Execute Behavior Tree XMLβ
MoveIt Pro also supports you programmatically generating the Behavior Tree XML and then execute it. You can send the entire XML contents to an action server. Python examples are provided here, but for now we will use the command line.
First, start MoveIt Pro and in a separate terminal enter a shell (moveit_pro shell
):
Install the action client interface in the shell instance:
sudo apt update && sudo apt install ros-humble-ros2action
Send the action request:
ros2 action send_goal /do_objective moveit_studio_sdk_msgs/action/DoObjectiveSequence '
objective_xml_string: |
<root BTCPP_format="4" main_tree_to_execute="test simple">
<BehaviorTree ID="test simple">
<Control ID="Sequence" name="TopLevelSequence">
<Action ID="AlwaysSuccess"/>
</Control>
</BehaviorTree>
</root>
'
You should get the following result from the action, indicating it was successful:
Waiting for an action server to become available...
Sending goal:
objective_name: ''
objective_xml_string: "<root BTCPP_format=\"4\" main_tree_to_execute=\"test simple\">\n <BehaviorTree ID=\"test simple\">\n <Control ID=\"Sequence\" name=\"TopLevelSequence\">\n <Action ID=\"AlwaysSuccess\"/>\n </Control>\n </BehaviorTree>\n</root>\n"
parameter_overrides: []
Goal accepted with ID: d5f48811588240f19a3ab303ccdc78ce
Result:
error_code:
val: 1
message: ''
source: ''
error_message: ''
Goal finished with status: SUCCEEDED
Summaryβ
This tutorial introduced advanced usage of MoveIt Pro for customizing and deploying robotic applications. You learned how to use the moveit_pro CLI, create and extend robot configuration packages, develop custom behaviors, and integrate with external ROS interfaces. You also saw how to run Objectives programmatically using the MoveIt Pro SDKβequipping you with the tools to build and deploy production-grade robotic systems.