Tesseract ROSCPP Interface¶
Note
This tutorial was heavily inspired from MoveIt! tutorials. Credit should be recognized for the tutorial structure and some text content.
For Tesseract, the most common interface will be through ROS with the tesseract_ros package. It provides functionality for common operations such as setting joint or pose goals, creating motion plans, moving the robot, adding objects into the environment, etc.
Getting Started¶
If you haven’t already done so, make sure you’ve completed the steps in Getting Started.
The Launch File¶
The entire launch file is here on GitHub
Running the Code¶
Open a terminal, and launch the executable and Rviz. Wait for Rviz to finish loading:
roslaunch tesseract_ros_examples basic_cartesian_example.launch
After Rviz opens, go back to the terminal where the launch file was executed, and the following text should be visible. Robot motion in Rviz begins immediately upon pressing any key.
[ERROR] [1613730481.601846614]: Hit enter key to continue!
Expected Output¶
In Rviz, the the robot should trace a trajectory in its environment on top of the cube object:
The Entire Code¶
The entire example code is written from one executable node that links against a BasicCartesianExample(…) class (installed as a library).
Executable Node:
BasicCartesianExample(…) Class:
Next we step through the code piece by piece. The basic_cartesian_example_node.cpp initializes ROS, instantiates the BasicCartesianExample(…) class, and calls the run() method from the class.
/**
* @file basic_cartesian_example_node.cpp
* @brief Basic cartesian example node
*
* @author Levi Armstrong
* @date July 22, 2019
* @version TODO
* @bug No known bugs
*
* @copyright Copyright (c) 2017, Southwest Research Institute
*
* @par License
* Software License Agreement (Apache License)
* @par
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* @par
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <tesseract_common/macros.h>
TESSERACT_COMMON_IGNORE_WARNINGS_PUSH
#include <ros/ros.h>
TESSERACT_COMMON_IGNORE_WARNINGS_POP
#include <tesseract_examples/basic_cartesian_example.h>
#include <tesseract_monitoring/environment_monitor.h>
#include <tesseract_rosutils/plotting.h>
using namespace tesseract_examples;
using namespace tesseract_rosutils;
/** @brief Default ROS parameter for robot description */
const std::string ROBOT_DESCRIPTION_PARAM = "robot_description";
/** @brief Default ROS parameter for robot description */
const std::string ROBOT_SEMANTIC_PARAM = "robot_description_semantic";
/** @brief RViz Example Namespace */
const std::string EXAMPLE_MONITOR_NAMESPACE = "tesseract_ros_examples";
int main(int argc, char** argv)
{
ros::init(argc, argv, "basic_cartesian_example_node");
ros::NodeHandle pnh("~");
ros::NodeHandle nh;
bool plotting = true;
bool rviz = true;
bool ifopt = false;
bool debug = false;
// Get ROS Parameters
pnh.param("plotting", plotting, plotting);
pnh.param("rviz", rviz, rviz);
pnh.param("ifopt", ifopt, ifopt);
pnh.param("debug", debug, debug);
// Initial setup
std::string urdf_xml_string, srdf_xml_string;
nh.getParam(ROBOT_DESCRIPTION_PARAM, urdf_xml_string);
nh.getParam(ROBOT_SEMANTIC_PARAM, srdf_xml_string);
auto env = std::make_shared<tesseract_environment::Environment>();
auto locator = std::make_shared<tesseract_rosutils::ROSResourceLocator>();
if (!env->init(urdf_xml_string, srdf_xml_string, locator))
exit(1);
// Create monitor
auto monitor = std::make_shared<tesseract_monitoring::ROSEnvironmentMonitor>(env, EXAMPLE_MONITOR_NAMESPACE);
if (rviz)
monitor->startPublishingEnvironment();
ROSPlottingPtr plotter;
if (plotting)
plotter = std::make_shared<ROSPlotting>(env->getSceneGraph()->getRoot());
BasicCartesianExample example(env, plotter, ifopt, debug);
example.run();
}
For the BasicCartesianExample(…) class within basic_cartesian_example.cpp, the translation unit is setup with using-declarations and required strings.
using namespace tesseract_environment;
using namespace tesseract_scene_graph;
using namespace tesseract_collision;
using namespace tesseract_rosutils;
using namespace tesseract_visualization;
/** @brief Default ROS parameter for robot description */
const std::string ROBOT_DESCRIPTION_PARAM = "robot_description";
/** @brief Default ROS parameter for robot description */
const std::string ROBOT_SEMANTIC_PARAM = "robot_description_semantic";
/** @brief RViz Example Namespace */
const std::string EXAMPLE_MONITOR_NAMESPACE = "tesseract_ros_examples";
The primary functionality is in the run() method of the class.
Get parameters from the ROS Parameter Server and pass them into the Tesseract environment (env_).
Create the environment monitor that understands contact points within the environment.
Create an octomap of a cube that is given to the environment monitor.
bool BasicCartesianExample::run()
{
using tesseract_planning::CartesianWaypoint;
using tesseract_planning::CompositeInstruction;
using tesseract_planning::CompositeInstructionOrder;
using tesseract_planning::Instruction;
using tesseract_planning::ManipulatorInfo;
using tesseract_planning::PlanInstruction;
using tesseract_planning::PlanInstructionType;
using tesseract_planning::ProcessPlanningFuture;
using tesseract_planning::ProcessPlanningRequest;
using tesseract_planning::ProcessPlanningServer;
using tesseract_planning::StateWaypoint;
using tesseract_planning::Waypoint;
using tesseract_planning_server::ROSProcessEnvironmentCache;
// Initial setup
std::string urdf_xml_string, srdf_xml_string;
nh_.getParam(ROBOT_DESCRIPTION_PARAM, urdf_xml_string);
nh_.getParam(ROBOT_SEMANTIC_PARAM, srdf_xml_string);
ResourceLocator::Ptr locator = std::make_shared<tesseract_rosutils::ROSResourceLocator>();
if (!env_->init<OFKTStateSolver>(urdf_xml_string, srdf_xml_string, locator))
return false;
// Create monitor
monitor_ = std::make_shared<tesseract_monitoring::EnvironmentMonitor>(env_, EXAMPLE_MONITOR_NAMESPACE);
if (rviz_)
monitor_->startPublishingEnvironment(tesseract_monitoring::EnvironmentMonitor::UPDATE_ENVIRONMENT);
// Create octomap and add it to the local environment
Command::Ptr cmd = addPointCloud();
if (!monitor_->applyCommand(cmd))
return false;
In the addPointCloud() method, we use the Point Cloud Library to create a cube. Then, the cube is transformed into an Octomap representation, and this is passed to the Tesseract environment.
Command::Ptr BasicCartesianExample::addPointCloud()
{
// Create octomap and add it to the local environment
pcl::PointCloud<pcl::PointXYZ> full_cloud;
double delta = 0.05;
auto length = static_cast<int>(1 / delta);
for (int x = 0; x < length; ++x)
for (int y = 0; y < length; ++y)
for (int z = 0; z < length; ++z)
full_cloud.push_back(pcl::PointXYZ(-0.5f + static_cast<float>(x * delta),
-0.5f + static_cast<float>(y * delta),
-0.5f + static_cast<float>(z * delta)));
sensor_msgs::PointCloud2 pointcloud_msg;
pcl::toROSMsg(full_cloud, pointcloud_msg);
octomap::Pointcloud octomap_data;
octomap::pointCloud2ToOctomap(pointcloud_msg, octomap_data);
std::shared_ptr<octomap::OcTree> octree = std::make_shared<octomap::OcTree>(2 * delta);
octree->insertPointCloud(octomap_data, octomap::point3d(0, 0, 0));
// Add octomap to environment
Link link_octomap("octomap_attached");
Visual::Ptr visual = std::make_shared<Visual>();
visual->origin = Eigen::Isometry3d::Identity();
visual->origin.translation() = Eigen::Vector3d(1, 0, 0);
visual->geometry = std::make_shared<tesseract_geometry::Octree>(octree, tesseract_geometry::Octree::BOX);
link_octomap.visual.push_back(visual);
Collision::Ptr collision = std::make_shared<Collision>();
collision->origin = visual->origin;
collision->geometry = visual->geometry;
link_octomap.collision.push_back(collision);
Joint joint_octomap("joint_octomap_attached");
joint_octomap.parent_link_name = "base_link";
joint_octomap.child_link_name = link_octomap.getName();
joint_octomap.type = JointType::FIXED;
return std::make_shared<tesseract_environment::AddLinkCommand>(link_octomap, joint_octomap);
}
Continuing in the run() method, we define motion parameters by:
Setting cartesian waypoints and asking the planner to perform freespace and linear moves for different programs.
Create a planning server that will solve each program for the robot’s joint kinematics.
Plot the trajectory in Rviz for animation.
// Create Program
CompositeInstruction program("cartesian_program", CompositeInstructionOrder::ORDERED, ManipulatorInfo("manipulator"));
// Start Joint Position for the program
Waypoint wp0 = StateWaypoint(joint_names, joint_pos);
PlanInstruction start_instruction(wp0, PlanInstructionType::START);
program.setStartInstruction(start_instruction);
// Create cartesian waypoint
Waypoint wp1 = CartesianWaypoint(Eigen::Isometry3d::Identity() * Eigen::Translation3d(0.5, -0.2, 0.62) *
Eigen::Quaterniond(0, 0, 1.0, 0));
Waypoint wp2 = CartesianWaypoint(Eigen::Isometry3d::Identity() * Eigen::Translation3d(0.5, 0.3, 0.62) *
Eigen::Quaterniond(0, 0, 1.0, 0));
// Plan freespace from start
PlanInstruction plan_f0(wp1, PlanInstructionType::FREESPACE, "freespace_profile");
plan_f0.setDescription("from_start_plan");
// Plan linear move
PlanInstruction plan_c0(wp2, PlanInstructionType::LINEAR, "RASTER");
// Plan freespace to end
PlanInstruction plan_f1(wp0, PlanInstructionType::FREESPACE, "freespace_profile");
plan_f1.setDescription("to_end_plan");
// Add Instructions to program
program.push_back(plan_f0);
program.push_back(plan_c0);
program.push_back(plan_f1);
ROS_INFO("basic cartesian plan example");
// Create Process Planning Server
ProcessPlanningServer planning_server(std::make_shared<ROSProcessEnvironmentCache>(monitor_), 5);
planning_server.loadDefaultProcessPlanners();
// Create Process Planning Request
ProcessPlanningRequest request;
request.name = tesseract_planning::process_planner_names::TRAJOPT_PLANNER_NAME;
request.instructions = Instruction(program);
// Print Diagnostics
request.instructions.print("Program: ");
// Solve process plan
ProcessPlanningFuture response = planning_server.run(request);
planning_server.waitForAll();
// Plot Process Trajectory
if (rviz_ && plotter != nullptr && plotter->isConnected())
{
plotter->waitForInput();
const auto* ci = response.results->cast_const<tesseract_planning::CompositeInstruction>();
tesseract_common::Toolpath toolpath = tesseract_planning::toToolpath(*ci, env_);
tesseract_common::JointTrajectory trajectory = tesseract_planning::toJointTrajectory(*ci);
plotter->plotMarker(ToolpathMarker(toolpath));
plotter->plotTrajectory(trajectory, env_->getStateSolver());
}
ROS_INFO("Final trajectory is collision free");
return true;
}
} // namespace tesseract_ros_examples