From 34ed82528a57d3fc8be45eb518459382166c4e33 Mon Sep 17 00:00:00 2001 From: JafarAbdi Date: Fri, 2 Jun 2023 20:21:57 +0000 Subject: [PATCH] Add python bindings --- CMakeLists.txt | 20 +++ README.md | 4 + launch/demo_rviz.launch.py | 29 ++++- package.xml | 2 + python/examples/moveit_visual_tools_demo.py | 131 ++++++++++++++++++++ python/moveit_visual_tools/__init__.py | 1 + python/src/moveit_visual_tools.cpp | 89 +++++++++++++ 7 files changed, 275 insertions(+), 1 deletion(-) create mode 100755 python/examples/moveit_visual_tools_demo.py create mode 100644 python/moveit_visual_tools/__init__.py create mode 100644 python/src/moveit_visual_tools.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e08aa6..10d82cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,4 +100,24 @@ if(BUILD_TESTING) ament_lint_auto_find_test_dependencies() endif() +#################### +## Python binding ## +#################### + +find_package(Python3 REQUIRED COMPONENTS Interpreter Development) +find_package(pybind11_vendor REQUIRED) +find_package(pybind11 REQUIRED) + +ament_python_install_package(moveit_visual_tools PACKAGE_DIR python/moveit_visual_tools) + +pybind11_add_module(pymoveit_visual_tools python/src/moveit_visual_tools.cpp) +target_link_libraries(pymoveit_visual_tools PUBLIC moveit_visual_tools) +ament_target_dependencies(pymoveit_visual_tools PUBLIC pybind11 rviz_visual_tools) +install(TARGETS pymoveit_visual_tools + LIBRARY DESTINATION ${PYTHON_INSTALL_DIR}/moveit_visual_tools) +install(PROGRAMS + python/examples/moveit_visual_tools_demo.py + DESTINATION lib/${PROJECT_NAME} +) + ament_package() diff --git a/README.md b/README.md index 555954d..39d391a 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,10 @@ To run some demos displaying robot states and collision objects: ros2 launch moveit_visual_tools demo_rviz.launch.py +To run the python binding demo: + + ros2 launch moveit_visual_tools demo_rviz.launch.py launch_python_demo:=true + ## Code API See [VisualTools Class Reference](http://docs.ros.org/kinetic/api/moveit_visual_tools/html/classmoveit__visual__tools_1_1MoveItVisualTools.html) diff --git a/launch/demo_rviz.launch.py b/launch/demo_rviz.launch.py index 5c08233..4ec8be0 100644 --- a/launch/demo_rviz.launch.py +++ b/launch/demo_rviz.launch.py @@ -32,6 +32,9 @@ from ament_index_python.packages import get_package_share_directory from launch import LaunchDescription from launch_ros.actions import Node +from launch.conditions import IfCondition, UnlessCondition +from launch.substitutions import LaunchConfiguration +from launch.actions import DeclareLaunchArgument def load_file(package_name, file_path): @@ -62,6 +65,18 @@ def generate_launch_description(): package="moveit_visual_tools", executable="moveit_visual_tools_demo", output="screen", + condition=UnlessCondition(LaunchConfiguration("launch_python_demo")), + parameters=[ + robot_description, + robot_description_semantic, + ], + ) + + moveit_visual_tools_demo_py = Node( + package="moveit_visual_tools", + executable="moveit_visual_tools_demo.py", + output="screen", + condition=IfCondition(LaunchConfiguration("launch_python_demo")), parameters=[ robot_description, robot_description_semantic, @@ -94,4 +109,16 @@ def generate_launch_description(): arguments=["0.0", "0.0", "0.0", "0.0", "0.0", "0.0", "world", "base"], ) - return LaunchDescription([rviz_node, static_tf, moveit_visual_tools_demo]) + return LaunchDescription( + [ + DeclareLaunchArgument( + "launch_python_demo", + default_value="False", + description="Launch the Python demo", + ), + rviz_node, + static_tf, + moveit_visual_tools_demo, + moveit_visual_tools_demo_py, + ] + ) diff --git a/package.xml b/package.xml index 72d54fd..2057040 100644 --- a/package.xml +++ b/package.xml @@ -29,6 +29,8 @@ trajectory_msgs visualization_msgs + pybind11_vendor + ament_lint_auto ament_lint_common diff --git a/python/examples/moveit_visual_tools_demo.py b/python/examples/moveit_visual_tools_demo.py new file mode 100755 index 0000000..7815d62 --- /dev/null +++ b/python/examples/moveit_visual_tools_demo.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 + +import moveit_visual_tools as mvt +import rviz_visual_tools as rvt +import sys +import time +from rclpy import logging +import numpy as np +from ament_index_python.packages import get_package_share_directory +from copy import deepcopy + +logger = logging.get_logger("moveit_visual_tools_demo") + +from geometry_msgs.msg import Pose, Point, Quaternion + +rvt.init() + +PLANNING_GROUP_NAME = "arm" + +node = rvt.RvizVisualToolsNode("moveit_visual_tools_demo") +node.start_spin_thread() +visual_tools = mvt.MoveItVisualTools(node, "world", "/moveit_visual_tools") +visual_tools.load_planning_scene_monitor() +visual_tools.load_marker_publisher(True) +visual_tools.load_robot_state_publisher("display_robot_state") +visual_tools.set_manual_scene_updating() + +robot_state = visual_tools.get_shared_robot_state() +robot_model = visual_tools.get_robot_model() +jmg = robot_model.get_joint_model_group(PLANNING_GROUP_NAME) + +visual_tools.delete_all_markers() +visual_tools.remove_all_collision_objects() +visual_tools.trigger_planning_scene_update() + +visual_tools.publish_text( + Pose(position=Point(x=0.0, y=0.0, z=4.0)), + "MoveIt-Visual-Tools", + scale=rvt.Scales.XXLARGE, +) +visual_tools.trigger() + +logger.info("Showing 5 random robot states") +for _ in range(5): + robot_state.set_to_random_positions(jmg) + visual_tools.publish_robot_state(robot_state) + time.sleep(1.0) + +logger.info("Showing 5 random robot states in different colors") +for _ in range(5): + robot_state.set_to_random_positions(jmg) + visual_tools.publish_robot_state(robot_state, color=visual_tools.get_random_color()) + time.sleep(1.0) + +logger.info("Hiding robot state") +visual_tools.hide_robot() +time.sleep(1.0) + +logger.info("Showing robot state") +visual_tools.publish_robot_state(robot_state, color=rvt.Colors.DEFAULT) +time.sleep(1.0) + +logger.info("Publishing Collision Floor") +visual_tools.publish_collision_floor(-0.5, "Floor", color=rvt.Colors.GREY) +visual_tools.trigger_planning_scene_update() +time.sleep(1.0) + +logger.info("Publishing Collision Wall") +visual_tools.publish_collision_wall( + -4.0, -1.0, 0.0, np.pi * 1.1, 6.0, 4.0, "Wall", color=rvt.Colors.GREY +) +visual_tools.trigger_planning_scene_update() +time.sleep(1.0) + + +logger.info("Publishing Collision Mesh") +mesh_pose = Pose() +for i in range(5): + visual_tools.publish_collision_mesh( + mesh_pose, + f"Mesh_{i}", + f"file://{get_package_share_directory('moveit_visual_tools')}/resources/demo_mesh.stl", + color=visual_tools.get_random_color(), + ) + mesh_pose.position.x += 0.4 + visual_tools.trigger_planning_scene_update() +time.sleep(1.0) + +logger.info("Publishing Collision Block") +block_pose = Pose(position=Point(y=1.0)) +for i in np.linspace(0.0, 1.0, 10): + visual_tools.publish_collision_block( + block_pose, + f"Block_{i}", + color=visual_tools.get_random_color(), + ) + block_pose.position.x += 0.4 + visual_tools.trigger_planning_scene_update() +time.sleep(1.0) + +logger.info("Publishing Collision Rectanglular Cuboid") +cuboid_min_size = 0.05 +cuboid_max_size = 0.2 +cuboid_point1 = Point(y=2.0) +for i in np.linspace(0.0, 1.0, 10): + cuboid_point2 = deepcopy(cuboid_point1) + cuboid_point2.x += i * cuboid_max_size + cuboid_min_size + cuboid_point2.y += (i * cuboid_max_size + cuboid_min_size) / 2.0 + cuboid_point2.z += i * cuboid_max_size + cuboid_min_size + visual_tools.publish_collision_cuboid( + cuboid_point1, cuboid_point2, f"Cuboid_{i}", visual_tools.get_random_color() + ) + cuboid_point1.x += 0.4 + visual_tools.trigger_planning_scene_update() +time.sleep(1.0) + +logger.info("Publishing Collision Cylinder") +cylinder_min_size = 0.01 +cylinder_max_size = 0.3 +cylinder_pose = Pose(position=Point(y=3.0)) +for i in np.linspace(0.0, 1.0, 10): + visual_tools.publish_collision_cylinder( + cylinder_pose, + height=i * cylinder_max_size + cylinder_min_size, + radius=i * cylinder_max_size + cylinder_min_size, + object_name=f"Cylinder_{i}", + color=visual_tools.get_random_color(), + ) + cylinder_pose.position.x += 0.1 + i + visual_tools.trigger_planning_scene_update() +time.sleep(1.0) diff --git a/python/moveit_visual_tools/__init__.py b/python/moveit_visual_tools/__init__.py new file mode 100644 index 0000000..8f737c4 --- /dev/null +++ b/python/moveit_visual_tools/__init__.py @@ -0,0 +1 @@ +from moveit_visual_tools.pymoveit_visual_tools import MoveItVisualTools diff --git a/python/src/moveit_visual_tools.cpp b/python/src/moveit_visual_tools.cpp new file mode 100644 index 0000000..44cde2a --- /dev/null +++ b/python/src/moveit_visual_tools.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +namespace py = pybind11; +using py::literals::operator""_a; + +namespace moveit_visual_tools +{ +PYBIND11_MODULE(pymoveit_visual_tools, m) +{ + py::module::import("rviz_visual_tools.pyrviz_visual_tools"); + py::module::import("moveit.core.robot_state"); + py::module::import("moveit.core.robot_model"); + py::module::import("moveit.core.planning_scene"); + + py::class_(m, "MoveItVisualTools") + .def(py::init( + [](const rviz_visual_tools::RvizVisualToolsNode::SharedPtr& node) { return MoveItVisualTools(node); })) + .def(py::init([](const rviz_visual_tools::RvizVisualToolsNode::SharedPtr& node, const std::string& base_frame, + const std::string& marker_topic) { return MoveItVisualTools(node, base_frame, marker_topic); }), + "node"_a, "base_frame"_a, "marker_topic"_a = rviz_visual_tools::RVIZ_MARKER_TOPIC) + .def("set_robot_state_topic", &MoveItVisualTools::setRobotStateTopic) + .def("set_planning_scene_topic", &MoveItVisualTools::setPlanningSceneTopic) + .def("load_planning_scene_monitor", &MoveItVisualTools::loadPlanningSceneMonitor) + .def("process_collision_object_msg", &MoveItVisualTools::processCollisionObjectMsg, "msg"_a, + "color"_a = rviz_visual_tools::Colors::GREEN) + .def("process_attached_collision_object_msg", &MoveItVisualTools::processAttachedCollisionObjectMsg) + .def("move_collision_object", + py::overload_cast( + &MoveItVisualTools::moveCollisionObject)) + .def("trigger_planning_scene_update", &MoveItVisualTools::triggerPlanningSceneUpdate) + .def("load_shared_robot_state", &MoveItVisualTools::loadSharedRobotState) + .def("get_shared_robot_state", &MoveItVisualTools::getSharedRobotState) + .def("get_root_robot_state", &MoveItVisualTools::getSharedRobotState) + .def("get_robot_model", &MoveItVisualTools::getRobotModel) + .def("load_ee_marker", &MoveItVisualTools::loadEEMarker) + .def("load_trajectory_publisher", &MoveItVisualTools::loadTrajectoryPub) + .def("load_robot_state_publisher", &MoveItVisualTools::loadRobotStatePub, "topic"_a, "blocking"_a = true) + .def("set_manual_scene_updating", &MoveItVisualTools::setManualSceneUpdating, "enable_manual"_a = true) + .def("publish_ee_markers", + py::overload_cast&, const rviz_visual_tools::Colors&, const std::string&>( + &MoveItVisualTools::publishEEMarkers), + "pose"_a, "ee_jmq"_a, "ee_joint_pos"_a = std::vector(), + "color"_a = rviz_visual_tools::Colors::DEFAULT, "ns"_a = "end_effector") + .def("publish_ik_solutions", &MoveItVisualTools::publishIKSolutions, "ik_solutions"_a, "arm_jmg"_a, + "display_time"_a = 0.4) + .def("remove_all_collision_objects", &MoveItVisualTools::removeAllCollisionObjects) + .def("cleanup_collision_object", &MoveItVisualTools::cleanupCO) + .def("cleanup_attached_collision_object", &MoveItVisualTools::cleanupACO) + .def("attach_collision_object", &MoveItVisualTools::attachCO) + .def("publish_collision_floor", &MoveItVisualTools::publishCollisionFloor, "z"_a = 0.0, "plane_name"_a = "Floor", + "color"_a = rviz_visual_tools::Colors::GREEN) + .def("publish_collision_block", &MoveItVisualTools::publishCollisionBlock, "pose"_a, "name"_a = "block", + "size"_a = 0.1, "color"_a = rviz_visual_tools::Colors::GREEN) + .def("publish_collision_cuboid", + py::overload_cast(&MoveItVisualTools::publishCollisionCuboid)) + .def("publish_collision_cuboid", + py::overload_cast(&MoveItVisualTools::publishCollisionCuboid)) + .def("publish_collision_cylinder", + py::overload_cast(&MoveItVisualTools::publishCollisionCylinder), + "object_pose"_a, "object_name"_a, "radius"_a, "height"_a, "color"_a = rviz_visual_tools::Colors::GREEN) + .def("publish_collision_mesh", + py::overload_cast(&MoveItVisualTools::publishCollisionMesh), + "object_pose"_a, "object_name"_a, "mesh_path"_a, "color"_a = rviz_visual_tools::Colors::GREEN) + .def("publish_collision_wall", + py::overload_cast(&MoveItVisualTools::publishCollisionWall), + "x"_a, "y"_a, "z"_a, "angle"_a = 0.0, "width"_a = 2.0, "height"_a = 1.5, "name"_a = "wall", + "color"_a = rviz_visual_tools::Colors::GREEN) + .def("publish_workspace_parameters", &MoveItVisualTools::publishWorkspaceParameters) + .def("publish_robot_state", + py::overload_cast&>(&MoveItVisualTools::publishRobotState), + "robot_state"_a, "color"_a = rviz_visual_tools::Colors::DEFAULT, + "highlight_links"_a = std::vector()) + .def("hide_robot", &MoveItVisualTools::hideRobot) + .def("show_joint_limits", &MoveItVisualTools::showJointLimits) + .def("get_planning_scene_monitor", &MoveItVisualTools::getPlanningSceneMonitor); +} +} // namespace moveit_visual_tools