Skip to content

Commit

Permalink
Moveit controller (#10)
Browse files Browse the repository at this point in the history
* Add parameter for twist message frame id.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Use new moveit_servo package.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Start on moveit controller.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Introduce moveit_action_server.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Introduce rviz controller.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Undo rename to rcdt_utilities_py.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Fix reuse.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Correct dependencies.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Add rviz file for markers.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Set default rviz_frame to world.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Move static_transform from moveit to robot launch file.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Fix reuse.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Improve argument names.

Signed-off-by: Jelmer de Wolde <[email protected]>

* Remove rviz marker publishing (should be handled else).

Signed-off-by: Jelmer de Wolde <[email protected]>

* Update readme.

Signed-off-by: Jelmer de Wolde <[email protected]>

---------

Signed-off-by: Jelmer de Wolde <[email protected]>
  • Loading branch information
Jelmerdw authored Nov 11, 2024
1 parent 35f5755 commit 3aa1881
Show file tree
Hide file tree
Showing 12 changed files with 719 additions and 50 deletions.
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@ SPDX-License-Identifier: Apache-2.0

# RCDT Utilities

This repository contains ROS2-based utility functions that can be used for ROS2 software development for any robot. This avoids duplication of often repeated code between different robotic specific repositories.
This repository contains ROS2-based utility functions and nodes that can be used for ROS2 software development for any robot. This avoids duplication of often repeated code between different robotic specific repositories.

## Moveit Controller Node

This node can be used to use code-wise Moveit control. For Franka:

`ros2 launch rcdt_franka franka.launch.py moveit:=node`

By selecting option *node* for the moveit flag, the moveit_controller node will be started. One can move the robot using an Action call, for example:

`ros2 action send_goal --feedback /moveit_controller rcdt_utilities_msgs/action/Moveit "{goal_pose: {header: {frame_id: fr3_link0}, pose: {position: {x: 0.28, y: 0.2, z: 0.5}, orientation: {x: 1.0, y: 0.0, z: 0.0, w: 0.0}}}}"`

## License

This project is licensed under the Apache License Version 2.0 - see [LICENSE](LICENSE) for details.

## Contributing

Please read CODE_OF_CONDUCT, CONTRIBUTING, and PROJECT GOVERNANCE located in the overarching [RCDT robotics](https://github.com/alliander-opensource/rcdt_robotics) project repository for details on the process for submitting pull requests to us.
Please read CODE_OF_CONDUCT, CONTRIBUTING, and PROJECT GOVERNANCE located in the overarching [RCDT robotics](https://github.com/alliander-opensource/rcdt_robotics) project repository for details on the process for submitting pull requests to us.
18 changes: 17 additions & 1 deletion rcdt_utilities/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ project(rcdt_utilities)

find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rclcpp REQUIRED)

find_package(std_srvs REQUIRED)
find_package(rcdt_utilities_msgs REQUIRED)

find_package(rviz_visual_tools REQUIRED)
find_package(tf2_eigen REQUIRED)

# Install project files
install(
Expand All @@ -16,9 +23,18 @@ install(
DESTINATION share/${PROJECT_NAME}
)

# Install nodes
# Install cpp:
add_executable(rviz_controller_node src/rviz_controller_node.cpp)
ament_target_dependencies(rviz_controller_node rclcpp std_srvs rcdt_utilities_msgs rviz_visual_tools tf2_eigen)

install(TARGETS
rviz_controller_node
DESTINATION lib/${PROJECT_NAME})

# Install python nodes:
install(PROGRAMS
nodes/joy_to_twist_node.py
nodes/moveit_controller_node.py
DESTINATION lib/${PROJECT_NAME}
)

Expand Down
72 changes: 50 additions & 22 deletions rcdt_utilities/launch/moveit.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,59 @@
from launch import LaunchDescription, LaunchContext, LaunchDescriptionEntity
from launch.actions import OpaqueFunction, ExecuteProcess
from launch_ros.actions import Node
from rcdt_utilities.launch_utils import (
LaunchArgument,
get_yaml,
get_file_path,
get_moveit_parameters,
)
from rcdt_utilities.launch_utils import LaunchArgument, get_yaml, get_file_path
from moveit_configs_utils import MoveItConfigsBuilder

SKIP = LaunchDescriptionEntity()

moveit_arg = LaunchArgument("moveit", "off", ["classic", "servo", "off"])
use_sim_arg = LaunchArgument("simulation", True, [True, False])
moveit_mode_arg = LaunchArgument("moveit", "off", ["node", "rviz", "servo", "off"])
moveit_config_package_arg = LaunchArgument(
"moveit_config_package", "rcdt_franka_moveit_config"
)
servo_params_package_arg = LaunchArgument("servo_params_package", "rcdt_franka")


def launch_setup(context: LaunchContext) -> None:
moveit_config_package = moveit_config_package_arg.value(context)
moveit_config = get_moveit_parameters("fr3", moveit_config_package)
use_sim = use_sim_arg.value(context)
moveit_mode = moveit_mode_arg.value(context)
config_package = moveit_config_package_arg.value(context)
servo_params_package = servo_params_package_arg.value(context)

moveit_classic = Node(
# Create moveit config dictionary:
moveit_config = MoveItConfigsBuilder(robot_name="fr3", package_name=config_package)
if moveit_mode == "node":
moveit_config.trajectory_execution(
get_file_path(config_package, ["config"], "moveit_controllers.yaml")
)
moveit_config.moveit_cpp(
get_file_path(config_package, ["config"], "planning_pipeline.yaml")
)
moveit_config = moveit_config.to_dict()

# Moveit as node:
moveit_node = Node(
package="rcdt_utilities",
executable="moveit_controller_node.py",
parameters=[moveit_config, {"use_sim_time": use_sim}],
)

# Moveit in rviz:
moveit_rviz = Node(
package="moveit_ros_move_group",
executable="move_group",
parameters=[moveit_config],
)

static_transform_publisher = Node(
package="tf2_ros",
executable="static_transform_publisher",
name="static_tf_world_base",
arguments=["--frame-id", "world", "--child-frame-id", "base"],
display_config_moveit = get_file_path("rcdt_utilities", ["rviz"], "moveit.rviz")
rviz = Node(
package="rviz2",
executable="rviz2",
arguments=["--display-config", display_config_moveit],
parameters=[moveit_config],
)

servo_params_package = servo_params_package_arg.value(context)
# Moveit servo:
file = get_file_path(servo_params_package, ["config"], "servo_params.yaml")
servo_config = get_yaml(file)
servo_params = {"moveit_servo": servo_config}
Expand All @@ -61,19 +82,26 @@ def launch_setup(context: LaunchContext) -> None:
shell=True,
)

skip = LaunchDescriptionEntity()
# Select moveit:
moveit_selector = {
"node": moveit_node,
"rviz": moveit_rviz,
"servo": moveit_servo,
"off": SKIP,
}
moveit = moveit_selector[moveit_mode]

return [
moveit_classic if moveit_arg.value(context) == "classic" else skip,
static_transform_publisher if moveit_arg.value(context) != "off" else skip,
moveit_servo if moveit_arg.value(context) == "servo" else skip,
set_servo_command_type if moveit_arg.value(context) == "servo" else skip,
moveit,
rviz if moveit_mode == "rviz" else SKIP,
set_servo_command_type if moveit_mode == "servo" else SKIP,
]


def generate_launch_description() -> LaunchDescription:
return LaunchDescription(
[
moveit_arg.declaration,
moveit_mode_arg.declaration,
moveit_config_package_arg.declaration,
servo_params_package_arg.declaration,
OpaqueFunction(function=launch_setup),
Expand Down
30 changes: 5 additions & 25 deletions rcdt_utilities/launch/rviz.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,25 @@
from launch import LaunchDescription, LaunchContext
from launch.actions import OpaqueFunction
from launch_ros.actions import Node
from rcdt_utilities.launch_utils import (
LaunchArgument,
get_moveit_parameters,
get_file_path,
)
from rcdt_utilities.launch_utils import LaunchArgument, get_file_path

display_config_general = get_file_path("rcdt_utilities", ["rviz"], "general.rviz")
display_config_moveit = get_file_path("rcdt_utilities", ["rviz"], "moveit.rviz")

rviz_frame_arg = LaunchArgument("rviz_frame", "")
rviz_load_moveit_arg = LaunchArgument("rviz_load_moveit", False, [True, False])
rviz_load_moveit_robot_arg = LaunchArgument("rviz_load_moveit_robot", "")
rviz_load_moveit_package_arg = LaunchArgument("rviz_load_moveit_package", "")
rviz_frame_arg = LaunchArgument("rviz_frame", "world")
rviz_display_config = LaunchArgument("rviz_display_config", display_config_general)


def launch_setup(context: LaunchContext) -> None:
arguments = []
arguments = ["--display-config", rviz_display_config.value(context)]

rviz_frame = rviz_frame_arg.value(context)
if rviz_frame != "":
arguments.extend(["-f", rviz_frame])

if rviz_load_moveit_arg.value(context):
robot_name = rviz_load_moveit_robot_arg.value(context)
package_name = rviz_load_moveit_package_arg.value(context)
moveit_parameters = get_moveit_parameters(robot_name, package_name)
parameters = [moveit_parameters]
arguments.extend(["--display-config", display_config_moveit])
else:
parameters = []
arguments.extend(["--display-config", display_config_general])

rviz = Node(
package="rviz2",
executable="rviz2",
arguments=arguments,
parameters=parameters,
)

return [rviz]
Expand All @@ -51,9 +33,7 @@ def generate_launch_description() -> LaunchDescription:
return LaunchDescription(
[
rviz_frame_arg.declaration,
rviz_load_moveit_arg.declaration,
rviz_load_moveit_robot_arg.declaration,
rviz_load_moveit_package_arg.declaration,
rviz_display_config.declaration,
OpaqueFunction(function=launch_setup),
]
)
109 changes: 109 additions & 0 deletions rcdt_utilities/nodes/moveit_controller_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env python3

# SPDX-FileCopyrightText: Alliander N. V.
#
# SPDX-License-Identifier: Apache-2.0

from typing import List

import rclpy
from rclpy.node import Node
from rclpy.action import ActionServer
from rclpy.action.server import ServerGoalHandle

from moveit.planning import MoveItPy, PlanningComponent
from moveit.core.robot_state import RobotState
from moveit.core.robot_model import RobotModel, JointModelGroup
from moveit.core.planning_interface import MotionPlanResponse
from moveit.core.controller_manager import ExecutionStatus
from rcdt_utilities_msgs.action import Moveit

from geometry_msgs.msg import PoseStamped


class MoveitControllerNode(Node):
def __init__(self, group: str) -> None:
name = "moveit_controller"
super().__init__(name)

self.group = group

self.robot = MoveItPy(node_name="moveit_py")
if not self.init_links(group):
return
self.planner: PlanningComponent = self.robot.get_planning_component(group)

self.server = ActionServer(self, Moveit, name, self.callback)

def init_links(self, group: str) -> bool:
robot_model: RobotModel = self.robot.get_robot_model()
joint_model_group: JointModelGroup = robot_model.get_joint_model_group(group)

end_effectors: List[JointModelGroup] = robot_model.end_effectors
if len(end_effectors) == 0:
self.get_logger().error("No end-effector was found. Exiting...")
return False
end_effector = end_effectors[0]
if len(end_effectors) > 1:
self.get_logger().warn(
f"Multiple end-effector defined. First one ('{end_effector.name}') is used."
)

self.ee_link = end_effector.link_model_names[0]
self.links: List[str] = joint_model_group.link_model_names

return True

def update_state(self) -> None:
self.planner.set_start_state_to_current_state()
self.state: RobotState = self.planner.get_start_state()

def callback(self, goal_handle: ServerGoalHandle) -> None:
goal: Moveit.Goal = goal_handle.request

self.update_state()
feedback = Moveit.Feedback()
feedback.start_pose = self.state.get_pose(self.ee_link)
goal_handle.publish_feedback(feedback)

success = self.move_to_pose(goal.goal_pose)
goal_handle.succeed() if success else goal_handle.abort()

self.update_state()
result = Moveit.Result()
result.end_pose = self.state.get_pose(self.ee_link)

return result

def plan_and_execute(self) -> bool:
plan: MotionPlanResponse = self.planner.plan()
if not plan:
self.get_logger().error("No motion plan was found. Aborting...")
return False

status: ExecutionStatus = self.robot.execute(plan.trajectory, controllers=[])
if status.status == "SUCCEEDED":
return True
else:
self.get_logger().error(f"Execution resulted in status `{status.status}`.")
return False

def move_to_pose(self, pose: PoseStamped) -> bool:
if pose.header.frame_id not in self.links:
self.get_logger().error(
f"frame_id '{pose.header.frame_id}' not one of links: {self.links}"
)
return False
self.planner.set_goal_state(pose_stamped_msg=pose, pose_link=self.ee_link)
return self.plan_and_execute()


def main(args: str = None) -> None:
rclpy.init(args=args)
moveit_controller = MoveitControllerNode("fr3_arm")
rclpy.spin(moveit_controller)
rclpy.shutdown()


if __name__ == "__main__":
main()
8 changes: 8 additions & 0 deletions rcdt_utilities/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ SPDX-License-Identifier: Apache-2.0
<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>ament_cmake_python</buildtool_depend>

<buildtool_depend>rosidl_default_generators</buildtool_depend>

<depend>rclcpp</depend>
<depend>std_srvs</depend>
<depend>rcdt_utilities_msgs</depend>
<depend>rviz_visual_tools</depend>
<depend>tf2_eigen</depend>

<test_depend>ament_lint_auto</test_depend>

<export>
Expand Down
Loading

0 comments on commit 3aa1881

Please sign in to comment.