lowbandwidth_trajectory_follower.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017, 2018 Jarek Potiuk (low bandwidth trajectory follower)
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
18 #include <endian.h>
19 #include <ros/ros.h>
20 #include <cmath>
21 
22 static const std::array<double, 6> EMPTY_VALUES = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
23 
24 static const std::string TIME_INTERVAL("{{TIME_INTERVAL}}");
25 static const std::string SERVOJ_TIME("{{SERVOJ_TIME}}");
26 static const std::string SERVOJ_TIME_WAITING("{{SERVOJ_TIME_WAITING}}");
27 static const std::string MAX_WAITING_TIME("{{MAX_WAITING_TIME}}");
28 static const std::string SERVOJ_GAIN("{{SERVOJ_GAIN}}");
29 static const std::string SERVOJ_LOOKAHEAD_TIME("{{SERVOJ_LOOKAHEAD_TIME}}");
30 static const std::string REVERSE_IP("{{REVERSE_IP}}");
31 static const std::string REVERSE_PORT("{{REVERSE_PORT}}");
32 static const std::string MAX_JOINT_DIFFERENCE("{{MAX_JOINT_DIFFERENCE}}");
33 static const std::string POSITION_PROGRAM = R"(
34 def driveRobotLowBandwidthTrajectory():
35  global JOINT_NUM = 6
36  global TIME_INTERVAL = {{TIME_INTERVAL}}
37  global SERVOJ_TIME = {{SERVOJ_TIME}}
38  global SERVOJ_TIME_WAITING = {{SERVOJ_TIME_WAITING}}
39  global MAX_WAITING_TIME = {{MAX_WAITING_TIME}}
40  global EMPTY_VALUES = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
41  global SERVOJ_GAIN = {{SERVOJ_GAIN}}
42  global SERVOJ_LOOKAHEAD_TIME = {{SERVOJ_LOOKAHEAD_TIME}}
43  global CONNECTION_NAME = "reverse_connection"
44  global REVERSE_IP = "{{REVERSE_IP}}"
45  global REVERSE_PORT = {{REVERSE_PORT}}
46  global MAX_JOINT_DIFFERENCE = {{MAX_JOINT_DIFFERENCE}}
47  global g_position_previous = EMPTY_VALUES
48  global g_position_target = EMPTY_VALUES
49  global g_position_next = EMPTY_VALUES
50  global g_velocity_previous = EMPTY_VALUES
51  global g_velocity_target = EMPTY_VALUES
52  global g_velocity_next = EMPTY_VALUES
53  global g_time_previous = 0.0
54  global g_time_target = 0.0
55  global g_time_next = 0.0
56  global g_num_previous = -1
57  global g_num_target = -1
58  global g_num_next = -1
59  global g_received_waypoints_number = -1
60  global g_requested_waypoints_number = -1
61  global g_total_elapsed_time = 0
62  global g_stopping = False
63  def send_message(message):
64  socket_send_string(message, CONNECTION_NAME)
65  socket_send_byte(10, CONNECTION_NAME)
66  end
67 
68  def is_waypoint_sentinel(waypoint):
69  local l_previous_index = 2
70  while l_previous_index < 1 + JOINT_NUM * 2 + 2:
71  if waypoint[l_previous_index] != 0.0:
72  return False
73  end
74  l_previous_index = l_previous_index + 1
75  end
76  return True
77  end
78 
79  def is_final_position_reached(position):
80  local l_current_position = get_actual_joint_positions()
81  local l_index = 0
82  while l_index < JOINT_NUM:
83  if norm(position[l_index] - l_current_position[l_index]) > MAX_JOINT_DIFFERENCE:
84  return False
85  end
86  l_index = l_index + 1
87  end
88  return True
89  end
90 
91  def interpolate(time_within_segment, total_segment_time, start_pos, l_end_pos, l_start_vel, end_vel):
92  local a = start_pos
93  local b = l_start_vel
94  local c = (-3 * a + 3 * l_end_pos - 2 * total_segment_time * b - total_segment_time * end_vel) / pow(total_segment_time, 2)
95  local d = (2 * a - 2 * l_end_pos + total_segment_time * b + total_segment_time * end_vel) / pow(total_segment_time, 3)
96  return a + b * time_within_segment + c * pow(time_within_segment, 2) + d * pow(time_within_segment, 3)
97  end
98 
99  def add_next_waypoint(waypoint):
100  enter_critical
101  g_position_previous = g_position_target
102  g_velocity_previous = g_velocity_target
103  g_time_previous = g_time_target
104  g_num_previous = g_num_target
105  g_position_target = g_position_next
106  g_velocity_target = g_velocity_next
107  g_time_target = g_time_next
108  g_num_target = g_num_next
109  g_num_next = waypoint[1]
110  g_position_next = [waypoint[2], waypoint[3], waypoint[4], waypoint[5], waypoint[6], waypoint[7]]
111  g_velocity_next = [waypoint[8], waypoint[9], waypoint[10], waypoint[11], waypoint[12], waypoint[13]]
112  g_time_next = waypoint[14]
113  g_received_waypoints_number = g_num_next
114  exit_critical
115  end
116 
117  thread controllingThread():
118  local l_received_waypoints_number = -1
119  local l_requested_waypoints_number = -1
120  local l_stopped = False
121  local l_current_position = get_actual_joint_positions()
122  enter_critical
123  g_requested_waypoints_number = 2
124  exit_critical
125  while True:
126  enter_critical
127  l_requested_waypoints_number = g_requested_waypoints_number
128  exit_critical
129  local l_max_waiting_time_left = MAX_WAITING_TIME
130  while l_received_waypoints_number < l_requested_waypoints_number and l_max_waiting_time_left > 0:
131  servoj(l_current_position,t=SERVOJ_TIME_WAITING,lookahead_time=SERVOJ_LOOKAHEAD_TIME,gain=SERVOJ_GAIN)
132  enter_critical
133  l_received_waypoints_number = g_received_waypoints_number
134  exit_critical
135  l_max_waiting_time_left = l_max_waiting_time_left - SERVOJ_TIME_WAITING
136  end
137  if l_max_waiting_time_left <= 0:
138  textmsg("Closing the connection on waiting too long.")
139  socket_close(CONNECTION_NAME)
140  halt
141  end
142  enter_critical
143  local l_start_pos = g_position_previous
144  local l_start_vel = g_velocity_previous
145  local l_start_time = g_time_previous
146  local l_start_num= g_num_previous
147  local l_end_pos = g_position_target
148  local l_end_vel = g_velocity_target
149  local l_end_time = g_time_target
150  local l_end_num = g_num_target
151  local l_total_elapsed_time = g_total_elapsed_time
152  local l_stopping_after_next_interpolation = g_stopping
153  g_requested_waypoints_number = g_requested_waypoints_number + 1
154  exit_critical
155 
156  l_current_position = l_start_pos
157 
158  local l_total_segment_time = l_end_time - l_start_time
159 
160  while l_total_elapsed_time <= l_end_time:
161  local l_segment_elapsed_time = l_total_elapsed_time - l_start_time
162  j = 0
163  while j < JOINT_NUM:
164  l_current_position[j] = interpolate(l_segment_elapsed_time, l_total_segment_time, l_start_pos[j], l_end_pos[j], l_start_vel[j], l_end_vel[j])
165  j = j + 1
166  end
167  servoj(l_current_position,t=SERVOJ_TIME,lookahead_time=SERVOJ_LOOKAHEAD_TIME,gain=SERVOJ_GAIN)
168  enter_critical
169  g_total_elapsed_time = g_total_elapsed_time + TIME_INTERVAL
170  l_total_elapsed_time = g_total_elapsed_time
171  exit_critical
172 
173  end
174  if l_stopping_after_next_interpolation:
175  while not is_final_position_reached(l_end_pos):
176  textmsg("Catching up on final position not reached first time.")
177  servoj(l_end_pos,t=SERVOJ_TIME,lookahead_time=SERVOJ_LOOKAHEAD_TIME,gain=SERVOJ_GAIN)
178  end
179  textmsg("Finishing the controlling thread. Final position reached.")
180  break
181  end
182  end
183  end
184 
185  thread sendingThread():
186  local controlling_thread = run controllingThread()
187  local l_sent_waypoints_number = -1
188  local l_requested_waypoints_number = -1
189  local l_stopping = False
190 
191  enter_critical
192  l_requested_waypoints_number = g_requested_waypoints_number
193  l_stopping = g_stopping
194  exit_critical
195  while not l_stopping:
196  while l_sent_waypoints_number == l_requested_waypoints_number and not l_stopping:
197  sleep(SERVOJ_TIME_WAITING)
198 
199  enter_critical
200  l_requested_waypoints_number = g_requested_waypoints_number
201  l_stopping = g_stopping
202  exit_critical
203 
204  end
205  if l_stopping:
206  break
207  end
208  send_message(l_sent_waypoints_number + 1)
209  l_sent_waypoints_number = l_sent_waypoints_number + 1
210  end
211  join controlling_thread
212  end
213 
214  thread receivingThread():
215  local sending_thread = run sendingThread()
216  while True:
217  waypoint_received = socket_read_ascii_float(14, CONNECTION_NAME)
218  if waypoint_received[0] == 0:
219  textmsg("Not received trajectory for the last 2 seconds. Quitting")
220  enter_critical
221  g_stopping = True
222  exit_critical
223  break
224  elif waypoint_received[0] != JOINT_NUM * 2 + 2:
225  textmsg("Received wrong number of floats in trajectory. This is certainly not OK.")
226  textmsg(waypoint_received[0])
227  enter_critical
228  g_stopping = True
229  exit_critical
230  break
231  elif is_waypoint_sentinel(waypoint_received):
232  add_next_waypoint(waypoint_received)
233  enter_critical
234  g_stopping = True
235  g_received_waypoints_number = g_received_waypoints_number + 1
236  exit_critical
237  break
238  end
239  add_next_waypoint(waypoint_received)
240  end
241  join sending_thread
242  end
243  socket_open(REVERSE_IP, REVERSE_PORT, CONNECTION_NAME)
244  receiving_thread = run receivingThread()
245  join receiving_thread
246  socket_close(CONNECTION_NAME)
247  textmsg("Exiting the program")
248 end
249 
250 )";
251 
253  int reverse_port, bool version_3)
254  : running_(false)
255  , commander_(commander)
256  , server_(reverse_port)
257  , time_interval_(0.008)
258  , servoj_time_(0.008)
259  , servoj_time_waiting_(0.001)
260  , max_waiting_time_(2.0)
261  , servoj_gain_(300.0)
262  , servoj_lookahead_time_(0.03)
263  , max_joint_difference_(0.01)
264 {
265  ros::param::get("~time_interval", time_interval_);
266  ros::param::get("~servoj_time", servoj_time_);
267  ros::param::get("~servoj_time_waiting", servoj_time_waiting_);
268  ros::param::get("~max_waiting_time", max_waiting_time_);
269  ros::param::get("~servoj_gain", servoj_gain_);
270  ros::param::get("~servoj_lookahead_time", servoj_lookahead_time_);
271  ros::param::get("~max_joint_difference", max_joint_difference_);
272 
273  std::string res(POSITION_PROGRAM);
274  std::ostringstream out;
275  if (!version_3)
276  {
277  LOG_ERROR("Low Bandwidth Trajectory Follower only works for interface version > 3");
278  std::exit(-1);
279  }
280  res.replace(res.find(TIME_INTERVAL), TIME_INTERVAL.length(), std::to_string(time_interval_));
281  res.replace(res.find(SERVOJ_TIME_WAITING), SERVOJ_TIME_WAITING.length(), std::to_string(servoj_time_waiting_));
282  res.replace(res.find(SERVOJ_TIME), SERVOJ_TIME.length(), std::to_string(servoj_time_));
283  res.replace(res.find(MAX_WAITING_TIME), MAX_WAITING_TIME.length(), std::to_string(max_waiting_time_));
284  res.replace(res.find(SERVOJ_GAIN), SERVOJ_GAIN.length(), std::to_string(servoj_gain_));
285  res.replace(res.find(SERVOJ_LOOKAHEAD_TIME), SERVOJ_LOOKAHEAD_TIME.length(), std::to_string(servoj_lookahead_time_));
286  res.replace(res.find(REVERSE_IP), REVERSE_IP.length(), reverse_ip);
287  res.replace(res.find(REVERSE_PORT), REVERSE_PORT.length(), std::to_string(reverse_port));
288  res.replace(res.find(MAX_JOINT_DIFFERENCE), MAX_JOINT_DIFFERENCE.length(), std::to_string(max_joint_difference_));
289  program_ = res;
290 
291  if (!server_.bind())
292  {
293  LOG_ERROR("Failed to bind server, the port %d is likely already in use", reverse_port);
294  std::exit(-1);
295  }
296  LOG_INFO("Low Bandwidth Trajectory Follower is initialized!");
297 }
298 
300 {
301  LOG_INFO("Starting LowBandwidthTrajectoryFollower");
302 
303  if (running_)
304  return true; // not sure
305 
306  LOG_INFO("Uploading trajectory program to robot");
307 
309  {
310  LOG_ERROR("Program upload failed!");
311  return false;
312  }
313 
314  LOG_DEBUG("Awaiting incoming robot connection");
315 
316  if (!server_.accept())
317  {
318  LOG_ERROR("Failed to accept incoming robot connection");
319  return false;
320  }
321 
322  LOG_DEBUG("Robot successfully connected");
323  return (running_ = true);
324 }
325 
326 bool LowBandwidthTrajectoryFollower::execute(const std::array<double, 6> &positions,
327  const std::array<double, 6> &velocities, double sample_number,
328  double time_in_seconds)
329 {
330  if (!running_)
331  return false;
332 
333  std::ostringstream out;
334 
335  out << "(";
336  out << sample_number << ",";
337  for (auto const &pos : positions)
338  {
339  out << pos << ",";
340  }
341  for (auto const &vel : velocities)
342  {
343  out << vel << ",";
344  }
345  out << time_in_seconds << ")\r\n";
346 
347  // I know it's ugly but it's the most efficient and fastest way
348  // We have only ASCII characters and we can cast char -> uint_8
349  const std::string tmp = out.str();
350  const char *formatted_message = tmp.c_str();
351  const uint8_t *buf = (uint8_t *)formatted_message;
352 
353  size_t written;
354  LOG_DEBUG("Sending message %s", formatted_message);
355 
356  return server_.write(buf, strlen(formatted_message) + 1, written);
357 }
358 
359 bool LowBandwidthTrajectoryFollower::execute(std::vector<TrajectoryPoint> &trajectory, std::atomic<bool> &interrupt)
360 {
361  if (!running_)
362  return false;
363 
364  bool finished = false;
365 
366  char *line[MAX_SERVER_BUF_LEN];
367 
368  bool res = true;
369 
370  while (!finished && !interrupt)
371  {
372  if (!server_.readLine((char *)line, MAX_SERVER_BUF_LEN))
373  {
374  LOG_DEBUG("Connection closed. Finishing!");
375  finished = true;
376  break;
377  }
378  unsigned int message_num = atoi((const char *)line);
379  LOG_DEBUG("Received request %i", message_num);
380  if (message_num < trajectory.size())
381  {
382  res = execute(trajectory[message_num].positions, trajectory[message_num].velocities, message_num,
383  trajectory[message_num].time_from_start.count() / 1e6);
384  }
385  else
386  {
387  res = execute(EMPTY_VALUES, EMPTY_VALUES, message_num, 0.0);
388  }
389  if (!res)
390  {
391  finished = true;
392  }
393  }
394  return res;
395 }
396 
398 {
399  if (!running_)
400  return;
401 
403  running_ = false;
404 }
bool accept()
Definition: server.cpp:75
static const std::string MAX_JOINT_DIFFERENCE("{{MAX_JOINT_DIFFERENCE}}")
static const std::string POSITION_PROGRAM
static const std::string REVERSE_IP("{{REVERSE_IP}}")
bool uploadProg(const std::string &s)
Definition: commander.cpp:41
bool execute(const std::array< double, 6 > &positions, const std::array< double, 6 > &velocities, double sample_number, double time_in_seconds)
static const std::string SERVOJ_GAIN("{{SERVOJ_GAIN}}")
static const std::string MAX_WAITING_TIME("{{MAX_WAITING_TIME}}")
static const std::string TIME_INTERVAL("{{TIME_INTERVAL}}")
#define LOG_DEBUG(format,...)
Definition: log.h:33
bool bind()
Definition: server.cpp:61
bool readLine(char *buffer, size_t buf_len)
Definition: server.cpp:110
bool write(const uint8_t *buf, size_t buf_len, size_t &written)
Definition: server.cpp:105
ROSCPP_DECL bool get(const std::string &key, std::string &s)
#define MAX_SERVER_BUF_LEN
Definition: server.h:29
static const std::string SERVOJ_TIME_WAITING("{{SERVOJ_TIME_WAITING}}")
#define LOG_INFO(format,...)
Definition: log.h:35
void disconnectClient()
Definition: server.cpp:97
static const std::string SERVOJ_TIME("{{SERVOJ_TIME}}")
static const std::string SERVOJ_LOOKAHEAD_TIME("{{SERVOJ_LOOKAHEAD_TIME}}")
LowBandwidthTrajectoryFollower(URCommander &commander, std::string &reverse_ip, int reverse_port, bool version_3)
#define LOG_ERROR(format,...)
Definition: log.h:36
static const std::array< double, 6 > EMPTY_VALUES
static const std::string REVERSE_PORT("{{REVERSE_PORT}}")


ur_modern_driver
Author(s): Thomas Timm Andersen, Simon Rasmussen
autogenerated on Fri Jun 26 2020 03:37:00