Instruction Executor example

This example shows how to use the Instruction Executor class. It can be used for easily executing a sequence of instructions such as motions on the robot using the built-in URScript functions.

The instruction_executor.cpp shows how to use this class:

Note

For the instruciton executor to work there has to be an established connection to the ReverseInterface. That means, the respective program has to be running on the robot. The example below will do that automatically, if the connected robot is in remote_control mode.

Listing 20 examples/instruction_executor.cpp
61  bool headless_mode = true;
62  g_my_robot = std::make_unique<urcl::ExampleRobotWrapper>(robot_ip, OUTPUT_RECIPE, INPUT_RECIPE, headless_mode,
63                                                           "external_control.urp");
64  if (!g_my_robot->getUrDriver()->checkCalibration(CALIBRATION_CHECKSUM))
65  {
66    URCL_LOG_ERROR("Calibration checksum does not match actual robot.");
67    URCL_LOG_ERROR("Use the ur_calibration tool to extract the correct calibration from the robot and pass that into "
68                   "the description. See "
69                   "[https://github.com/UniversalRobots/Universal_Robots_ROS_Driver#extract-calibration-information] "
70                   "for details.");
71  }
72  if (!g_my_robot->isHealthy())
73  {
74    URCL_LOG_ERROR("Something in the robot initialization went wrong. Exiting. Please check the output above.");
75    return 1;
76  }
77
78  auto instruction_executor = std::make_shared<urcl::InstructionExecutor>(g_my_robot->getUrDriver());

At first, a InstructionExecutor object is created with the URDriver object as it needs that for communication with the robot.

Currently, the InstructionExecutor can either be used to run sequences of motions or single motions.

Execute a sequence of motions

To run a sequence of motions, create an std::vector<std::shared_ptr<urcl::cointrol::MotionPrimitive>> and pass it to the executeMotion function:

Listing 21 examples/instruction_executor.cpp
 82  // Trajectory definition
 83  std::vector<std::shared_ptr<urcl::control::MotionPrimitive>> motion_sequence{
 84    std::make_shared<urcl::control::MoveJPrimitive>(urcl::vector6d_t{ -1.57, -1.57, 0, 0, 0, 0 }, 0.1,
 85                                                    std::chrono::seconds(5)),
 86    // This point uses acceleration / velocity parametrization
 87    std::make_shared<urcl::control::MoveJPrimitive>(urcl::vector6d_t{ -1.57, -1.6, 1.6, -0.7, 0.7, 0.2 }, 0.1,
 88                                                    std::chrono::seconds(0), 1.4, 2.0),
 89
 90    std::make_shared<urcl::control::MoveLPrimitive>(urcl::Pose(-0.203, 0.263, 0.559, 0.68, -1.083, -2.076), 0.1,
 91                                                    std::chrono::seconds(2)),
 92    std::make_shared<urcl::control::MoveLPrimitive>(urcl::Q{ -1.57, -1.6, 1.6, -0.7, 0.7, 0.2 }, 0.1,
 93                                                    std::chrono::seconds(2)),
 94    std::make_shared<urcl::control::MovePPrimitive>(urcl::Pose{ -0.203, 0.463, 0.559, 0.68, -1.083, -2.076 }, 0.1, 0.4,
 95                                                    0.4),
 96    std::make_shared<urcl::control::OptimoveJPrimitive>(urcl::vector6d_t{ -1.57, -1.57, 1.6, -0.5, 0.4, 0.3 }, 0.1, 0.4,
 97                                                        0.7),
 98    std::make_shared<urcl::control::OptimoveLPrimitive>(urcl::Pose(-0.203, 0.263, 0.559, 0.68, -1.083, -2.076), 0.1,
 99                                                        0.4, 0.7),
100  };
101  instruction_executor->executeMotion(motion_sequence);

Each element in the motion sequence can be a different motion type. In the example, there are two MoveJ motions, a MoveL motion, a MoveP motion, a OptimiveJ motion and a OptimoveL motion. The primitives’ parameters are directly forwarded to the underlying script functions, so the parameter descriptions for them apply, as well. Particularly, you may want to choose between either a time-based execution or an acceleration / velocity parametrization for some move functions. The latter will be ignored if a time > 0 is given.

Each motion primitive can be constructed either with its “native” target type (vector6d_t for MoveJ / OptimoveJ, urcl::Pose for MoveL / MoveP / MoveC / OptimoveL) or with a urcl::MotionTarget, which can hold either a urcl::Q (joint configuration) or a urcl::Pose (Cartesian pose). This makes it possible, for example, to use a MoveLPrimitive with a joint configuration – the robot will then compute the forward kinematics on the controller and linearly interpolate in tool space towards the resulting pose.

Please refer to the script manual for details.

Execute a single motion

To run a single motion, the InstructionExecutor provides the methods moveJ(...), moveL(...), moveP(...), moveC(...), optimoveJ(...) and optimoveL(...):

Listing 22 examples/instruction_executor.cpp
103  double goal_time_sec = 2.0;
104
105  // acceleration / velocity parametrization brace-init style will be interpreted as joint
106  // positions
107  instruction_executor->moveJ({ -1.742, -1.726, -2.214, -0.773, 1.572, -0.171 }, 2.0, 2.0);
108
109  // Passing a pose to moveJ will make it internally solve inverse kinematics.
110  instruction_executor->moveJ(urcl::Pose{ -0.206, -0.6437, 0.202, 0.0, 3.140, 0.0 }, 2.0, 2.0);
111
112  // To provide a q_near hint for the IK solver a joint configuration near the target can be added to a pose.
113  urcl::Pose target_pose{ -0.206, -0.6437, 0.202, 0.0, 3.140, 0.0 };
114  target_pose.setQNear(urcl::Q{ -1.7, -4, 1.5, -1, 1.5, 0 });
115  instruction_executor->moveJ(target_pose, 2.0, 2.0);
116
117  // q_near can also be set directly upon pose construction
118  instruction_executor->moveJ(
119      urcl::Pose{ -0.206, -0.6437, 0.202, 0.0, 3.140, 0.0, urcl::Q{ -1.7, -4, 1.5, -1, 1.5, 0 } }, 2.0, 2.0);
120
121  // goal time parametrization -- acceleration and velocity will be scaled to meed the goal time.
122  instruction_executor->moveJ({ -1.57, -1.6, 1.6, -0.7, 0.7, 0.2 }, 0.1, 0.1, goal_time_sec);
123
124  // moveL calls with brace-init style is interpreted as a pose
125  instruction_executor->moveL({ -0.0203, 0.363, 0.559, 0.68, -1.083, -2.076 }, 1.5, 1.5);
126  // A pose can also be explicitly passed to moveL
127  instruction_executor->moveL(urcl::Pose{ -0.0203, 0.363, 0.559, 0.68, -1.083, -2.076 }, 1.5, 1.5);
128  // moveL can also accept a joint position target, if explicitly wrapped into a urcl::Q object
129  instruction_executor->moveL(urcl::Q{ -1.572, -1.686, 1.707, -0.833, 0.782, 0.479 }, 1.5, 1.5, goal_time_sec);
130
131  // moveJ can also accept a Cartesian pose, when given explicitly
132  instruction_executor->moveJ(urcl::Pose{ -0.0203, 0.363, 0.559, 0.68, -1.083, -2.076 }, 0.1, 0.1, goal_time_sec);
133
134  // moveP can also be called with brace-init (interpreted as pose) or explicitly using a Pose or Q
135  instruction_executor->moveP({ -0.2, 0.363, 0.559, 0.68, -1.083, -2.076 }, 0.2, 0.2);
136  instruction_executor->moveP(urcl::Pose{ -0.0203, 0.303, 0.559, 0.68, -1.083, -2.076 }, 0.2, 0.2);
137  instruction_executor->moveP(urcl::Q{ -1.57, -1.83, 1.707, -0.833, 0.782, 0.479 }, 0.2, 0.2);
138
139  // For moveC via and target can be a Pose or Q. When brace-init style lists are given, values are
140  // interpreted as Pose.
141  instruction_executor->moveP(urcl::Pose{ -0.2, 0.5, 0.5, 0.68, -1.083, -2.076 }, 0.2, 0.2);
142  instruction_executor->moveC(urcl::Pose{ -0.2, 0.4, 0.4, 0.68, -1.083, -2.076 },
143                              urcl::Pose{ -0.2, 0.4, 0.6, 0.68, -1.083, -2.076 });
144  instruction_executor->moveC(urcl::Pose{ -0.2, 0.5, 0.5, 0.68, -1.083, -2.076 },
145                              urcl::Q{ -1.84, -1.8, 2.7, 4.2, 0.6, -4.0 });
146
147  // For optimove functions, the same target rules as for moveJ and moveL apply.
148  instruction_executor->optimoveJ({ -1.57, -1.6, 1.6, -0.7, 0.7, 0.2 }, 1.0, 1.0);
149  instruction_executor->optimoveL(urcl::Pose{ -0.0203, 0.363, 0.559, 0.68, -1.083, -2.076 }, 1.0, 1.0);
150  instruction_executor->optimoveL(urcl::Q{ -1.572, -1.686, 1.707, -0.833, 0.782, 0.479 }, 1.0, 1.0);
151  instruction_executor->optimoveJ(urcl::Pose{ -0.0203, 0.363, 0.559, 0.68, -1.083, -2.076 }, 1.0, 1.0);

Again, time parametrization has priority over acceleration / velocity parameters.

Each motion function has two overloads: one taking the “natural” target type (vector6d_t for moveJ / optimoveJ, urcl::Pose for the other motions), and one taking a urcl::MotionTarget. Passing a braced-initializer-list of six doubles selects the natural overload, so moveJ({ ... }) continues to be interpreted as joint positions and moveL({ ... }) as a Cartesian pose. To select the other interpretation, wrap the values in urcl::Q{...} or urcl::Pose{...} explicitly – for example, moveJ(urcl::Pose{...}) performs a movej towards a Cartesian target and moveL(urcl::Q{...}) performs a movel towards the pose implied by a joint configuration. The same rules apply to moveP, moveC, optimoveJ and optimoveL.

For Cartesian targets, urcl::Pose may optionally set q_near (a urcl::Q): a joint-space hint forwarded on the trajectory socket for inverse kinematics. The bundled external-control URScript uses it for movej / optimovej to a pose; see Trajectory Point Interface.