-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implemented spline interpolation in joint space #103
Changes from all commits
00d568b
cec0847
ff3cf76
dce10ba
72f4fab
588e4c2
b19dbc7
8c0f5c5
146db88
e919b46
a84d86c
8a5f6d8
b5214f8
0992492
45792e7
02ed066
6625547
fe56323
3806f9e
ddaf62d
438f0d6
df3f81a
dafbdb8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
// this is for emacs file handling -*- mode: c++; indent-tabs-mode: nil -*- | ||
|
||
// -- BEGIN LICENSE BLOCK ---------------------------------------------- | ||
// Copyright 2023 Universal Robots A/S | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// -- END LICENSE BLOCK ------------------------------------------------ | ||
|
||
#include <ur_client_library/control/trajectory_point_interface.h> | ||
#include <ur_client_library/ur/dashboard_client.h> | ||
#include <ur_client_library/ur/ur_driver.h> | ||
#include <ur_client_library/types.h> | ||
|
||
#include <iostream> | ||
#include <memory> | ||
|
||
using namespace urcl; | ||
|
||
// In a real-world example it would be better to get those values from command line parameters / a | ||
// better configuration system such as Boost.Program_options | ||
const std::string DEFAULT_ROBOT_IP = "192.168.56.101"; | ||
const std::string SCRIPT_FILE = "resources/external_control.urscript"; | ||
const std::string OUTPUT_RECIPE = "examples/resources/rtde_output_recipe.txt"; | ||
const std::string INPUT_RECIPE = "examples/resources/rtde_input_recipe.txt"; | ||
const std::string CALIBRATION_CHECKSUM = "calib_12788084448423163542"; | ||
|
||
std::unique_ptr<UrDriver> g_my_driver; | ||
std::unique_ptr<DashboardClient> g_my_dashboard; | ||
vector6d_t g_joint_positions; | ||
|
||
void SendTrajectory(const std::vector<vector6d_t>& p_p, const std::vector<vector6d_t>& p_v, | ||
const std::vector<vector6d_t>& p_a, const std::vector<double>& time, bool use_spline_interpolation_) | ||
{ | ||
URCL_LOG_INFO("Starting joint-based trajectory forward"); | ||
g_my_driver->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_START, p_p.size()); | ||
|
||
for (size_t i = 0; i < p_p.size() && p_p.size() == time.size() && p_p[i].size() == 6; i++) | ||
{ | ||
// MoveJ | ||
if (!use_spline_interpolation_) | ||
{ | ||
g_my_driver->writeTrajectoryPoint(p_p[i], false, time[i]); | ||
} | ||
else // Use spline interpolation | ||
{ | ||
// QUINTIC | ||
if (p_v.size() == time.size() && p_a.size() == time.size() && p_v[i].size() == 6 && p_a[i].size() == 6) | ||
{ | ||
g_my_driver->writeTrajectorySplinePoint(p_p[i], p_v[i], p_a[i], time[i]); | ||
} | ||
// CUBIC | ||
else if (p_v.size() == time.size() && p_v[i].size() == 6) | ||
{ | ||
g_my_driver->writeTrajectorySplinePoint(p_p[i], p_v[i], time[i]); | ||
} | ||
else | ||
{ | ||
g_my_driver->writeTrajectorySplinePoint(p_p[i], time[i]); | ||
} | ||
} | ||
} | ||
URCL_LOG_INFO("Finished Sending Trajectory"); | ||
} | ||
|
||
// We need a callback function to register. See UrDriver's parameters for details. | ||
void handleRobotProgramState(bool program_running) | ||
{ | ||
// Print the text in green so we see it better | ||
std::cout << "\033[1;32mProgram running: " << std::boolalpha << program_running << "\033[0m\n" << std::endl; | ||
} | ||
|
||
// Callback function for trajectory execution. | ||
bool g_trajectory_running(false); | ||
void handleTrajectoryState(control::TrajectoryResult state) | ||
{ | ||
// trajectory_state = state; | ||
g_trajectory_running = false; | ||
std::string report = "?"; | ||
switch (state) | ||
{ | ||
case control::TrajectoryResult::TRAJECTORY_RESULT_SUCCESS: | ||
report = "success"; | ||
break; | ||
case control::TrajectoryResult::TRAJECTORY_RESULT_CANCELED: | ||
report = "canceled"; | ||
break; | ||
case control::TrajectoryResult::TRAJECTORY_RESULT_FAILURE: | ||
default: | ||
report = "failure"; | ||
break; | ||
} | ||
std::cout << "\033[1;32mTrajectory report: " << report << "\033[0m\n" << std::endl; | ||
} | ||
|
||
int main(int argc, char* argv[]) | ||
{ | ||
urcl::setLogLevel(urcl::LogLevel::INFO); | ||
|
||
// Parse the ip arguments if given | ||
std::string robot_ip = DEFAULT_ROBOT_IP; | ||
if (argc > 1) | ||
{ | ||
robot_ip = std::string(argv[1]); | ||
} | ||
|
||
// Making the robot ready for the program by: | ||
// Connect the the robot Dashboard | ||
g_my_dashboard.reset(new DashboardClient(robot_ip)); | ||
if (!g_my_dashboard->connect()) | ||
{ | ||
URCL_LOG_ERROR("Could not connect to dashboard"); | ||
return 1; | ||
} | ||
|
||
// Stop program, if there is one running | ||
if (!g_my_dashboard->commandStop()) | ||
{ | ||
URCL_LOG_ERROR("Could not send stop program command"); | ||
return 1; | ||
} | ||
|
||
// Power it off | ||
if (!g_my_dashboard->commandPowerOff()) | ||
{ | ||
URCL_LOG_ERROR("Could not send Power off command"); | ||
return 1; | ||
} | ||
|
||
// Power it on | ||
if (!g_my_dashboard->commandPowerOn()) | ||
{ | ||
URCL_LOG_ERROR("Could not send Power on command"); | ||
return 1; | ||
} | ||
Comment on lines
+133
to
+145
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What exactly is the motivation behind this? I found it rather annoying while testing with a real robot. If we reduce this to only switching the robot an and releasing the brakes, it would have the same effect that we know the robot will be running afterwards without the additional switch off / switch on thing. |
||
|
||
// Release the brakes | ||
if (!g_my_dashboard->commandBrakeRelease()) | ||
{ | ||
URCL_LOG_ERROR("Could not send BrakeRelease command"); | ||
return 1; | ||
} | ||
|
||
// Now the robot is ready to receive a program | ||
|
||
std::unique_ptr<ToolCommSetup> tool_comm_setup; | ||
const bool HEADLESS = true; | ||
g_my_driver.reset(new UrDriver(robot_ip, SCRIPT_FILE, OUTPUT_RECIPE, INPUT_RECIPE, &handleRobotProgramState, HEADLESS, | ||
std::move(tool_comm_setup), CALIBRATION_CHECKSUM)); | ||
g_my_driver->setKeepaliveCount(5); // decrease realtime requirements since example is running in | ||
// CI | ||
|
||
g_my_driver->registerTrajectoryDoneCallback(&handleTrajectoryState); | ||
|
||
// Once RTDE communication is started, we have to make sure to read from the interface buffer, as | ||
// otherwise we will get pipeline overflows. Therefore, do this directly before starting your main | ||
// loop. | ||
|
||
g_my_driver->startRTDECommunication(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With having this line so far away from the main loop it is probably not really deterministic whether we will be able to read once the second data package is sent. As the current joint positions are used in assembling the trajectory we can't simply move it down. One approach could be to start RTDE communication in a separate thread. |
||
|
||
std::unique_ptr<rtde_interface::DataPackage> data_pkg = g_my_driver->getDataPackage(); | ||
|
||
if (data_pkg) | ||
{ | ||
// Read current joint positions from robot data | ||
if (!data_pkg->getData("actual_q", g_joint_positions)) | ||
{ | ||
// This throwing should never happen unless misconfigured | ||
std::string error_msg = "Did not find 'actual_q' in data sent from robot. This should not happen!"; | ||
throw std::runtime_error(error_msg); | ||
} | ||
} | ||
|
||
const unsigned number_of_points = 5; | ||
const double s_pos[number_of_points] = { 4.15364583e-03, 4.15364583e-03, -1.74088542e-02, 1.44817708e-02, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There seems to be an error in unit conversion. The motions done by the robot are in fact a lot larger than this:
Edit: Nevermind. This seems to work correct. It's just that the position / velocity combination inside the test is a bit unintuitive. And in fact, it seems to move the robot into its joint limits inside the tests. I would suggest using a simpler motion instead. Additionally, I would suggest using a fixed motion and no relative motion to the starting state as this will have a much more predictable output. |
||
0.0 }; | ||
const double s_vel[number_of_points] = { -2.01015625e-01, 4.82031250e-02, 1.72812500e-01, -3.49453125e-01, | ||
8.50000000e-02 }; | ||
const double s_acc[number_of_points] = { 2.55885417e+00, -4.97395833e-01, 1.71276042e+00, -5.36458333e-02, | ||
-2.69817708e+00 }; | ||
const double s_time[number_of_points] = { 1.0000000e+00, 4.00000000e+00, 8.00100000e+00, 2.50000000e+01 / 2, | ||
4.00000000e+00 }; | ||
|
||
bool ret = false; | ||
URCL_LOG_INFO("Switch to Forward mode"); | ||
ret = g_my_driver->writeJointCommand(vector6d_t(), comm::ControlMode::MODE_FORWARD); | ||
if (!ret) | ||
{ | ||
URCL_LOG_ERROR("Could not send joint command. Is the robot in remote control?"); | ||
return 1; | ||
} | ||
|
||
std::vector<vector6d_t> p, v, a; | ||
std::vector<double> time; | ||
|
||
unsigned int joint_to_control = 5; | ||
for (unsigned i = 0; i < number_of_points; ++i) | ||
{ | ||
vector6d_t p_i = g_joint_positions; | ||
p_i[joint_to_control] = s_pos[i]; | ||
p.push_back(p_i); | ||
|
||
vector6d_t v_i; | ||
v_i[joint_to_control] = s_vel[i]; | ||
v.push_back(v_i); | ||
|
||
vector6d_t a_i; | ||
a_i[joint_to_control] = s_acc[i]; | ||
a.push_back(a_i); | ||
|
||
time.push_back(s_time[i]); | ||
} | ||
|
||
// CUBIC | ||
SendTrajectory(p, v, std::vector<vector6d_t>(), time, true); | ||
|
||
g_trajectory_running = true; | ||
while (g_trajectory_running) | ||
{ | ||
std::unique_ptr<rtde_interface::DataPackage> data_pkg = g_my_driver->getDataPackage(); | ||
if (data_pkg) | ||
{ | ||
// Read current joint positions from robot data | ||
if (!data_pkg->getData("actual_q", g_joint_positions)) | ||
{ | ||
// This throwing should never happen unless misconfigured | ||
std::string error_msg = "Did not find 'actual_q' in data sent from robot. This should not happen!"; | ||
throw std::runtime_error(error_msg); | ||
} | ||
bool ret = g_my_driver->writeJointCommand(vector6d_t(), comm::ControlMode::MODE_FORWARD); | ||
|
||
if (!ret) | ||
{ | ||
URCL_LOG_ERROR("Could not send joint command. Is the robot in remote control?"); | ||
return 1; | ||
} | ||
} | ||
} | ||
|
||
URCL_LOG_INFO("CUBIC Movement done"); | ||
|
||
// QUINTIC | ||
SendTrajectory(p, v, a, time, true); | ||
ret = g_my_driver->writeJointCommand(vector6d_t(), comm::ControlMode::MODE_FORWARD); | ||
|
||
g_trajectory_running = true; | ||
while (g_trajectory_running) | ||
{ | ||
std::unique_ptr<rtde_interface::DataPackage> data_pkg = g_my_driver->getDataPackage(); | ||
if (data_pkg) | ||
{ | ||
// Read current joint positions from robot data | ||
if (!data_pkg->getData("actual_q", g_joint_positions)) | ||
{ | ||
// This throwing should never happen unless misconfigured | ||
std::string error_msg = "Did not find 'actual_q' in data sent from robot. This should not happen!"; | ||
throw std::runtime_error(error_msg); | ||
} | ||
bool ret = g_my_driver->writeJointCommand(vector6d_t(), comm::ControlMode::MODE_FORWARD); | ||
|
||
if (!ret) | ||
{ | ||
URCL_LOG_ERROR("Could not send joint command. Is the robot in remote control?"); | ||
return 1; | ||
} | ||
} | ||
} | ||
|
||
URCL_LOG_INFO("QUINTIC Movement done"); | ||
|
||
ret = g_my_driver->writeJointCommand(vector6d_t(), comm::ControlMode::MODE_FORWARD); | ||
if (!ret) | ||
{ | ||
URCL_LOG_ERROR("Could not send joint command. Is the robot in remote control?"); | ||
return 1; | ||
} | ||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There could be an assertion here that
p_p
has the same size astime
, as this is a hard requirement.