For this exercise, work independently to compute \([x, y, z]\) of the robot's end effector in terms of the lengths of the robot's two linkages \((l_1, l_2)\) and the angles of rotation of the two revolute joints \( (q_1, q_2) \). If it's useful to you, feel free to reference the slides we just went over in class.
Once you've finished computing \([x, y, z]\), please wait for others to finish. We'll go over the solution together as a class.
The goal of this exercise is to start getting to know the OpenManipulatorX arm that we'll be using on our Turtlebot4. you will (1) learn the basics of turtlebot's arms though GUIs, (2) learn how to manipulate the arm through code. We'll be using the arm in simulation for this exercise, you'll get to use it on the physical Turtlebot4 for the first time during tomorrow's lab.
Before getting started on this exercise, you'll need to download and build the OpenManipulator ROS2 packages in your intro_robo_ws:
$ cd ~/intro_robo_ws/src
$ git clone -b humble https://github.com/ROBOTIS-GIT/open_manipulator.git
$ git clone https://github.com/Intro-Robotics-UChicago-Fall-2025/omx_cpp_interface.git
$ cd ~/intro_robo_ws
$ colcon build --symlink-install
$ source install/setup.bash
Let's now startup an OpenManipulatorX arm in simulation and control it with the OpenManipulatorX GUI.
$ ros2 launch open_manipulator_x_bringup gazebo.launch.py
This should bring up a Gazebo window with an OpenManipulatorX arm that looks like this:
$ ros2 launch open_manipulator_x_moveit_config move_group.launch.py
You should see a lot of output in the terminal, concluding with the phrase, You can start planning now!
$ ros2 launch open_manipulator_x_gui open_manipulator_x_gui.launch.py
This should bring up a Gazebo window with an OpenManipulatorX arm that looks like the image below. In order to start using the GUI, make sure to click the "Timer Start" button on the top right.
The GUI provides the following information:
Let's make sure your simulation robot can move its arm. Press Time Start and then click on Home Pose. You should see your robot arm move to a new position in the simulation. Pay close attention to how the new pose changes the joint and XYZ values.
Manual movement of the arm is possible through the options in the bottom right features of the GUI. There are two options available for this purpose,
Task Space Control : Sets the position of the red square between the robot's grippers to a
certain XYZ. Joint Space Control : Sets the position of each single join. The window with an s next to it is for selecting how fast you want the the transformation to occur. Once you are done with your manual selection, send your desired positions to the robot and watch the arm move.
The following gif shows a demonstration of using the OpenManipulatorX GUI to control the arm in simulation.
For your first set of homework deliverables, submit screenshots that include both the GUI and simulated OpenManipulatorX arm in Gazebo for the following:
Home Pose with the Gripper closed.X, Y, Z location of the end effector that you've specified.For this exercise, you will construct a new ROS2 package, from which you will send ROS2 messages that will control the OpenManipulatorX arm in simulation.
To run your code, follow these steps:
$ ros2 launch open_manipulator_x_bringup gazebo.launch.py
$ ros2 launch open_manipulator_x_moveit_config move_group.launch.py
$ ros2 run omx_cpp_interface arm_cmd
$ ros2 run [your_package_name] [your_node_name]
omx_cpp_interfaceThe omx_cpp_interface package provides a simple C++ interface to control the OpenManipulatorX arm using the MoveIt package. I (Sarah) wrote this package because MoveIt 2, a powerful motion planning framework, can only be used in ROS2 in C++ (and not Python). You do not need to implement anything in this package, but you should understand how to use it to control the arm through code.
The omx_cpp_interface package subscribes to the following ROS2 topics that you can publish to from your Python code (assuming that you're connected to Turtlebot number 11):
| ROS2 Topic | Message Type | Description |
|---|---|---|
/tb11/target_pose |
geometry_msgs/Pose |
You can publish a target end-effector pose message to this topic and the omx_cpp_interface will use it to set the target pose of the end effector. |
/tb11/target_joint_angles |
omx_cpp_interface/ArmJointAngles |
You can publish desired joint angles of the arm and the omx_cpp_interface will use these angles to move to the specified configuration. |
/tb11/target_gripper_position |
omx_cpp_interface/ArmGripperPosition |
You can publish the desired gripper locations of the left and right gripper components and the omx_cpp_interface will use these locations to move the gripper. |
The following code is an example of a ROS2 Python node that publishes to the /tbXX/target_gripper_position topic to set the gripper position of the OpenManipulatorX arm. You can use this code as a starting point for your own code to control the arm.
import os
import time
import rclpy
from rclpy.node import Node
from omx_cpp_interface.msg import ArmGripperPosition
class GripperPublisher(Node):
def __init__(self):
# initialize the node
super().__init__('gripper_publisher')
# get the ROS_DOMAIN_ID aka robot number
ros_domain_id = os.getenv("ROS_DOMAIN_ID", "0")
try:
if int(ros_domain_id) < 10:
ros_domain_id = "0" + str(int(ros_domain_id))
else:
ros_domain_id = str(int(ros_domain_id))
except Exception:
ros_domain_id = "00"
self.get_logger().info(f'ROS_DOMAIN_ID: {ros_domain_id}')
# create publisher to the target_gripper_position topic
topic = f'/tb{ros_domain_id}/target_gripper_position'
self.pub = self.create_publisher(ArmGripperPosition, topic, 10)
def publish_once(self):
# publish a message to set the gripper position
msg = ArmGripperPosition()
msg.left_gripper = 0.005
msg.right_gripper = 0.005
self.pub.publish(msg)
self.get_logger().info(f'Published ArmGripperPosition: left={msg.left_gripper} right={msg.right_gripper}')
def main(args=None):
# initialize the node
rclpy.init(args=args)
node = GripperPublisher()
# wait a couple seconds to ensure connections are established
time.sleep(2)
# publish the gripper position
try:
node.publish_once()
# spin once to allow the outgoing message to be delivered
rclpy.spin_once(node, timeout_sec=1.0)
except KeyboardInterrupt:
pass
# shut down the node
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Your goal in this exercise is to create a new ROS2 Python package that contains a node that publishes to the topics outlined in the table above to control both the position of the arm and the gripper.
Using your code, submit the following screenshots that contain both your terminal with your code running and the Gazebo simulation window showing the OpenManipulatorX arm in the desired position:
For your Class Meeting 9 Homework, submit the following via Canvas/Gradescope by 2:00pm on Wednesday, October 29th:
I want to thank Woolfrey for their helpful Youtube videos on forward and inverse kinematics for helping to inform the content for today's class.