Trajectory Joint Interface example

At first, we create a UrDriver object as usual:

Listing 68 examples/trajectory_point_interface.cpp
66  bool headless_mode = true;
67  g_my_robot = std::make_unique<urcl::ExampleRobotWrapper>(robot_ip, OUTPUT_RECIPE, INPUT_RECIPE, headless_mode,
68                                                           "external_control.urp");
69  if (!g_my_robot->isHealthy())
70  {
71    URCL_LOG_ERROR("Something in the robot initialization went wrong. Exiting. Please check the output above.");
72    return 1;
73  }

We use a small helper function to make sure that the reverse interface is active and connected before proceeding.

Initialization

As trajectory execution will be triggered asynchronously, we define a callback function to handle a finished trajectory. A trajectory is considered finished when the robot is no longer executing it, independent of whether it successfully reached its final point. The trajectory result will reflect whether it was executed successfully, was canceled upon request or failed for some reason.

Listing 69 examples/trajectory_point_interface.cpp
50void trajDoneCallback(const urcl::control::TrajectoryResult& result)
51{
52  URCL_LOG_INFO("Trajectory done with result %s", urcl::control::trajectoryResultToString(result).c_str());
53  g_trajectory_done = true;
54}

That callback can be registered at the UrDriver object:

Listing 70 examples/trajectory_point_interface.cpp
76  g_my_robot->getUrDriver()->registerTrajectoryDoneCallback(&trajDoneCallback);

MoveJ Trajectory

Then, in order to execute a trajectory, we need to define a trajectory as a sequence of points and parameters. The following example shows execution of a 2-point trajectory using URScript’s movej function:

Listing 71 examples/trajectory_point_interface.cpp
 80  {
 81    g_trajectory_done = false;
 82    // Trajectory definition
 83    std::vector<urcl::vector6d_t> points{ { -1.57, -1.57, 0, 0, 0, 0 }, { -1.57, -1.6, 1.6, -0.7, 0.7, 0.2 } };
 84    std::vector<double> motion_durations{ 5.0, 2.0 };
 85    std::vector<double> velocities{ 2.0, 2.3 };
 86    std::vector<double> accelerations{ 2.5, 2.5 };
 87    std::vector<double> blend_radii{ 0.1, 0.1 };
 88
 89    // Trajectory execution
 90    g_my_robot->getUrDriver()->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_START,
 91                                                             static_cast<int>(points.size() * 2));
 92    for (size_t i = 0; i < points.size(); i++)
 93    {
 94      g_my_robot->getUrDriver()->writeTrajectoryPoint(points[i], false, static_cast<float>(motion_durations[i]),
 95                                                      static_cast<float>(blend_radii[i]));
 96    }
 97
 98    // Same motion, but parametrized with acceleration and velocity
 99    motion_durations = { 0.0, 0.0 };
100    for (size_t i = 0; i < points.size(); i++)
101    {
102      g_my_robot->getUrDriver()->writeTrajectoryPoint(
103          points[i], static_cast<float>(accelerations[i]), static_cast<float>(velocities[i]), false,
104          static_cast<float>(motion_durations[i]), static_cast<float>(blend_radii[i]));
105    }
106
107    while (!g_trajectory_done)
108    {
109      std::this_thread::sleep_for(std::chrono::milliseconds(100));
110      g_my_robot->getUrDriver()->writeTrajectoryControlMessage(
111          urcl::control::TrajectoryControlMessage::TRAJECTORY_NOOP);
112    }
113  }

In fact, the path is followed twice, once parametrized by a segment duration and once using maximum velocity / acceleration settings. If a duration > 0 is given for a segment, the velocity and acceleration settings will be ignored as in the underlying URScript functions. In the example above, each of the g_my_robot->getUrDriver()->writeTrajectoryPoint() calls will be translated into a movej command in URScript.

While the trajectory is running, we need to tell the robot program that connection is still active and we expect the trajectory to be running. This is being done by the g_my_robot->getUrDriver()->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_NOOP); call.

MoveL Trajectory

Similar to the movej-based trajectory, execution can be done interpolating in joint space:

Listing 72 examples/trajectory_point_interface.cpp
118  {
119    g_trajectory_done = false;
120    // Trajectory definition
121    std::vector<urcl::vector6d_t> points{ { -0.203, 0.263, 0.559, 0.68, -1.083, -2.076 },
122                                          { -0.203, 0.463, 0.559, 0.68, -1.083, -2.076 } };
123    std::vector<double> motion_durations{ 5.0, 5.0 };
124    std::vector<double> velocities{ 2.0, 2.3 };
125    std::vector<double> accelerations{ 2.5, 2.5 };
126    std::vector<double> blend_radii{ 0.0, 0.0 };
127
128    // Trajectory execution of the path that goes through the points twice.
129    g_my_robot->getUrDriver()->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_START,
130                                                             static_cast<int>(points.size() * 2));
131    for (size_t i = 0; i < points.size(); i++)
132    {
133      // setting the cartesian parameter makes it interpret the 6d vector as a pose and use movel
134      g_my_robot->getUrDriver()->writeTrajectoryPoint(points[i], true, static_cast<float>(motion_durations[i]),
135                                                      static_cast<float>(blend_radii[i]));
136    }
137
138    // Same motion, but parametrized with acceleration and velocity
139    motion_durations = { 0.0, 0.0 };
140    for (size_t i = 0; i < points.size(); i++)
141    {
142      g_my_robot->getUrDriver()->writeTrajectoryPoint(
143          points[i], static_cast<float>(accelerations[i]), static_cast<float>(velocities[i]), true,
144          static_cast<float>(motion_durations[i]), static_cast<float>(blend_radii[i]));
145    }
146
147    while (!g_trajectory_done)
148    {
149      std::this_thread::sleep_for(std::chrono::milliseconds(100));
150      g_my_robot->getUrDriver()->writeTrajectoryControlMessage(
151          urcl::control::TrajectoryControlMessage::TRAJECTORY_NOOP);
152    }
153  }

Spline based interpolation

Similar to the Spline Interpolation example, the trajectory point interface can be used to execute motions using the spline interpolation:

Listing 73 examples/trajectory_point_interface.cpp
158  {
159    g_trajectory_done = false;
160    // Trajectory definition
161    std::vector<urcl::vector6d_t> positions{ { -1.57, -1.57, 0, 0, 0, 0 },
162                                             { -1.57, -1.57, -1.57, 0, 0, 0 },
163                                             { -1.57, -1.57, 0, 0, 0, 0 },
164                                             { -1.57, -1.6, 1.6, -0.7, 0.7, 0.2 } };
165    std::vector<urcl::vector6d_t> velocities{
166      { 0, 0, 0.0, 0, 0, 0 }, { 0, 0, 0.0, 0, 0, 0 }, { 0, 0, 1.5, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }
167    };
168    std::vector<double> motion_durations{ 3.0, 3.0, 3.0, 3.0 };
169
170    // Trajectory execution
171    g_my_robot->getUrDriver()->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_START,
172                                                             static_cast<int>(positions.size()));
173    for (size_t i = 0; i < positions.size(); i++)
174    {
175      g_my_robot->getUrDriver()->writeTrajectorySplinePoint(positions[i], velocities[i],
176                                                            static_cast<float>(motion_durations[i]));
177    }
178
179    while (!g_trajectory_done)
180    {
181      std::this_thread::sleep_for(std::chrono::milliseconds(100));
182      g_my_robot->getUrDriver()->writeTrajectoryControlMessage(
183          urcl::control::TrajectoryControlMessage::TRAJECTORY_NOOP);
184    }
185  }