KukaFRIdriver.hpp
Go to the documentation of this file.
1 #ifndef _KUKA_FRI_DRIVER
2 #define _KUKA_FRI_DRIVER
3 
4 #include <boost/asio.hpp>
5 #include <boost/circular_buffer.hpp>
6 #include <boost/config.hpp>
7 #include <boost/exception/all.hpp>
8 #include <chrono>
9 #include <memory>
10 #include <thread>
11 #include <tuple>
12 
13 #include <boost/math/special_functions/sign.hpp>
14 #include <boost/range/algorithm/copy.hpp>
15 #include <boost/range/algorithm/transform.hpp>
16 
17 #ifdef BOOST_NO_CXX11_ATOMIC_SMART_PTR
18 #include <boost/thread.hpp>
19 #endif
20 
21 // friClientData is found in the kuka connectivity FRI cpp zip file
22 #include "friClientData.h"
23 #include "friClientIf.h"
24 #include "grl/exception.hpp"
26 
27 #include "Kuka.hpp"
28 #include "KukaFRI.hpp"
29 
30 struct KukaState;
31 
32 /// @todo move somewhere that won't cause conflicts
33 template <typename T, size_t N>
34 std::ostream &operator<<(std::ostream &out,
35  const boost::container::static_vector<T, N> &v) {
36  out << "[";
37  size_t last = v.size() - 1;
38  for (size_t i = 0; i < v.size(); ++i) {
39  out << v[i];
40  if (i != last)
41  out << ", ";
42  }
43  out << "]";
44  return out;
45 }
46 
47 namespace grl {
48 namespace robot {
49 namespace arm {
50 
51 /// @brief internal function to decode KUKA FRI message buffer (using nanopb
52 /// decoder) for the KUKA FRI
53 ///
54 /// @note encode needs to be updated for each additional supported command type
55 /// and when updating to newer FRI versions
56 void decode(KUKA::FRI::ClientData &friData, std::size_t msg_size) {
57  // The decoder was given a pointer to the monitoringMessage at initialization
58  if (!friData.decoder.decode(friData.receiveBuffer, msg_size)) {
59  BOOST_THROW_EXCEPTION(std::runtime_error("Error decoding received data"));
60  }
61 
62  // check message type
63  if (friData.expectedMonitorMsgID !=
64  friData.monitoringMsg.header.messageIdentifier) {
65  BOOST_THROW_EXCEPTION(std::invalid_argument(
66  std::string("KukaFRI.hpp: Problem reading buffer, id code: ") +
67  boost::lexical_cast<std::string>(
68  static_cast<int>(friData.monitoringMsg.header.messageIdentifier)) +
69  std::string(" does not match expected id code: ") +
70  boost::lexical_cast<std::string>(
71  static_cast<int>(friData.expectedMonitorMsgID)) +
72  std::string("\n")));
73  return;
74  }
75 
76  friData.lastState =
77  grl::robot::arm::get(friData.monitoringMsg, KUKA::FRI::ESessionState());
78 }
79 
80 /// @brief Default LowLevelStepAlgorithmType
81 /// This algorithm is designed to be changed out
82 /// @todo Generalize this class using C++ techinques "tag dispatching" and "type
83 /// traits". See boost.geometry access and coorinate_type classes for examples
85 
86  /// Default constructor
87  /// @todo verify this doesn't corrupt the state of the system
88  LinearInterpolation() : armState(KukaState()) {}
89 
90  LinearInterpolation(const KukaState &armState_)
91  : armState(armState_),
92  goal_position_command_time_duration_remaining(
93  armState_.goal_position_command_time_duration) {
94  boost::copy(armState_.velocity_limits, std::back_inserter(velocity_limits));
95  };
96 
97  // no action by default
98  template <typename ArmDataType, typename CommandModeType>
99  void operator()(ArmDataType &, CommandModeType &) {
100  // need to tag dispatch here
101  }
102 
103  /// @bug motion interpolation and scaling doesn't seem to move in quite the
104  /// right way, it is much slower and doesn't go to the right place.
105  template <typename ArmData>
106  void operator()(ArmData &friData,
108  // switch (friData_->monitoringMsg.robotInfo.controlMode) {
109  // case ControlMode_POSITION_CONTROLMODE:
110  // case ControlMode_JOINT_IMPEDANCE_CONTROLMODE:
111 
112  KukaState::joint_state ipoJointPos;
113  KukaState::joint_state currentJointPos;
114  KukaState::joint_state diffToGoal;
115  KukaState::joint_state amountToMove;
116  KukaState::joint_state commandToSend;
117 
118  double rcurrentJointPos[7];
119  double rcommandedGoal[7];
120  double rdiffToGoal[7];
121  double ramountToMove[7];
122  double rcommandToSend[7];
123  double rvelocity_limits[7];
124 
125  // the current "holdposition" joint angles
126  /// @todo maybe this should be the
127  /// revolute_joint_angle_interpolated_open_chain_state_tag()? @see
128  /// kukaFRIalgorithm.hpp
129  grl::robot::arm::copy(friData.monitoringMsg,
130  std::back_inserter(currentJointPos),
132  boost::copy(currentJointPos, &rcurrentJointPos[0]);
133 
134  // single timestep in ms
135  int thisTimeStepMS(
136  grl::robot::arm::get(friData.monitoringMsg, grl::time_step_tag()));
137  double thisTimeStepS = (static_cast<double>(thisTimeStepMS) / 1000);
138  // double secondsPerTick =
139  // std::chrono::duration_cast<std::chrono::seconds>(thisTimeStep).count();
140 
141  // the fraction of the distance to the goal that should be traversed this
142  // tick
143  double fractionOfDistanceToTraverse =
144  static_cast<double>(thisTimeStepMS) /
145  static_cast<double>(goal_position_command_time_duration_remaining);
146 
147  boost::copy(armState.commandedPosition_goal, &rcommandedGoal[0]);
148  // get the angular distance to the goal
149  // use current time and time to destination to interpolate (scale) goal
150  // joint position
151  boost::transform(armState.commandedPosition_goal, currentJointPos,
152  std::back_inserter(diffToGoal),
153  [&](double commanded_angle, double current_angle) {
154  return (commanded_angle - current_angle) *
155  fractionOfDistanceToTraverse;
156  });
157  boost::copy(diffToGoal, &rdiffToGoal[0]);
158 
159  // decrease the time remaining by the current time step
160  goal_position_command_time_duration_remaining -= thisTimeStepMS;
161 
162  /// @todo correctly pass velocity limits from outside, use "copy" fuction in
163  /// Kuka.hpp, correctly account for differing robot models. This *should*
164  /// be in KukaFRIdriver at the end of this file.
165 
166  // R820 velocity limits
167  // A1 - 85 °/s == 1.483529864195 rad/s
168  // A2 - 85 °/s == 1.483529864195 rad/s
169  // A3 - 100 °/s == 1.745329251994 rad/s
170  // A4 - 75 °/s == 1.308996938996 rad/s
171  // A5 - 130 °/s == 2.268928027593 rad/s
172  // A6 - 135 °/s == 2.356194490192 rad/s
173  // A1 - 135 °/s == 2.356194490192 rad/s
174  KukaState::joint_state velocity_limits;
175  velocity_limits.push_back(1.483529864195); // RK: removed secondsPerTick
176  velocity_limits.push_back(1.483529864195);
177  velocity_limits.push_back(1.745329251994);
178  velocity_limits.push_back(1.308996938996);
179  velocity_limits.push_back(2.268928027593);
180  velocity_limits.push_back(2.356194490192);
181  velocity_limits.push_back(2.356194490192);
182 
183  boost::copy(velocity_limits, &rvelocity_limits[0]);
184  // use std::min to ensure commanded change in position remains under the
185  // maximum possible velocity for a single timestep
186  boost::transform(
187  diffToGoal, velocity_limits, std::back_inserter(amountToMove),
188  [&](double diff, double maxvel) {
189  return boost::math::copysign(std::min(std::abs(diff), maxvel), diff);
190  });
191 
192  boost::copy(amountToMove, &ramountToMove[0]);
193 
194  // add the current joint position to the amount to move to get the actual
195  // position command to send
196  boost::transform(currentJointPos, amountToMove,
197  std::back_inserter(commandToSend), std::plus<double>());
198 
199  boost::copy(commandToSend, &rcommandToSend[0]);
200 
201  // send the command
202  grl::robot::arm::set(friData.commandMsg, commandToSend,
204  // break;
205  }
206 
207  /// @todo look in FRI_Client_SDK_Cpp.zip to see if position must be set for
208  /// joint torques. Ref files: LBRTorqueSineOverlayClient.cpp,
209  /// LBRTorqueSineOverlayClient.h, friLBRCommand.cpp, friLBRCommand.h
210  template <typename ArmData>
211  void operator()(ArmData &friData,
213 
214  // case ControlMode_JOINT_IMPEDANCE_CONTROLMODE:
215  grl::robot::arm::set(friData.commandMsg, armState.commandedTorque,
217 
218  /// @note encode() needs to be updated for each additional supported command
219  /// type
220  // break;
221  }
222 
223  /// @todo look in FRI_Client_SDK_Cpp.zip to see if position must be set for
224  /// cartesian wrench. Ref files: LBRWrenchSineOverlayClient.cpp,
225  /// LBRWrenchSineOverlayClient.h, friLBRCommand.cpp, friLBRCommand.h
226  template <typename ArmData>
227  void operator()(ArmData &friData, cartesian_wrench_command_tag) {
228  // case ControlMode_CARTESIAN_IMPEDANCE_CONTROLMODE:
229  // not yet supported
230  grl::robot::arm::set(friData.commandMsg,
233  // break;
234  }
235 
236  /// @todo make this accessible via a nonmember function
237  bool hasCommandData() {
238  /// @todo check if duration remaining should be greater than zero or greater
239  /// than the last tick size
240  return goal_position_command_time_duration_remaining > 0;
241  }
242  // template<typename ArmData>
243  // void operator()(ArmData& clientData,
244  // revolute_joint_angle_open_chain_command_tag){
245  // default:
246  // break;
247  // }
248 private:
249  // the armstate at initialization of this object
250  KukaState armState;
251  KukaState::joint_state velocity_limits;
252  double goal_position_command_time_duration_remaining; // milliseconds
253 };
254 
255 /// @brief encode data in the class KUKA::FRI::ClientData into the send buffer
256 /// for the KUKA FRI.
257 /// this preps the information for transport over the network
258 ///
259 /// @note encode needs to be updated for each additional supported command type
260 /// and when updating to newer FRI versions
261 template <typename LowLevelStepAlgorithmType = LinearInterpolation>
262 std::size_t encode(LowLevelStepAlgorithmType &step_alg,
263  KUKA::FRI::ClientData &friData,
264  boost::system::error_code &ec) {
265  // reset send counter
266  friData.lastSendCounter = 0;
267 
268  // set sequence counters
269  friData.commandMsg.header.sequenceCounter = friData.sequenceCounter++;
270  friData.commandMsg.header.reflectedSequenceCounter =
271  friData.monitoringMsg.header.sequenceCounter;
272 
273  KUKA::FRI::ESessionState sessionState =
274  grl::robot::arm::get(friData.monitoringMsg, KUKA::FRI::ESessionState());
275 
276  if ((step_alg.hasCommandData() &&
277  (sessionState == KUKA::FRI::COMMANDING_WAIT ||
278  sessionState == KUKA::FRI::COMMANDING_ACTIVE))) {
280  friData.monitoringMsg, KUKA::FRI::EClientCommandMode());
281  switch (commandMode) {
282  case ClientCommandMode_POSITION:
283  step_alg(friData, revolute_joint_angle_open_chain_command_tag());
284  break;
285  case ClientCommandMode_WRENCH:
286  step_alg(friData, cartesian_wrench_command_tag());
287  break;
288  case ClientCommandMode_TORQUE:
290  break;
291  default:
292  // this is unhandled at the moment...
293  BOOST_VERIFY(false);
294  // BOOST_THROW_EXCEPTION_CURRENT_FUNCTION;
295  // ClientCommandMode_NO_COMMAND_MODE, anything else that is added in the
296  // future and unimplemented
297  /// @todo do nothing if in an unsupported command mode? Or do the same as
298  /// the next else if step?
299  break;
300  }
301 
302  } else if (!(friData.commandMsg.has_commandData &&
303  step_alg.hasCommandData() &&
304  (sessionState == KUKA::FRI::COMMANDING_WAIT ||
305  sessionState == KUKA::FRI::COMMANDING_ACTIVE))) {
306  // copy current measured joint position to commanded position only if we
307  // *don't* have new command data
308 
309  /// @todo should this be different if it is in torque mode?
310  /// @todo allow copying of data directly between commandmsg and
311  /// monitoringMsg
312  std::vector<double> msg;
313  copy(friData.monitoringMsg, std::back_inserter(msg),
315  // copy the previously recorded command over
316  set(friData.commandMsg, msg,
318  }
319 
320  int buffersize = KUKA::FRI::FRI_COMMAND_MSG_MAX_SIZE;
321  if (!friData.encoder.encode(friData.sendBuffer, buffersize)) {
322  // @todo figure out PB_GET_ERROR, integrate with error_code type supported
323  // by boost
324  ec = boost::system::errc::make_error_code(boost::system::errc::bad_message);
325  return 0;
326  }
327 
328  return buffersize;
329 }
330 
331 /// @brief Actually talk over the network to receive an update and send out a
332 /// new KUKA FRI command
333 ///
334 /// Receives an update, performs the necessary checks, then sends a message if
335 /// appropriate.
336 ///
337 /// @pre socket must already have the endpoint resolved and "connected". While
338 /// udp is technically stateless the asio socket supports the connection api
339 /// components for convenience.
340 template <typename LowLevelStepAlgorithmType = LinearInterpolation>
341 void update_state(boost::asio::ip::udp::socket &socket,
342  LowLevelStepAlgorithmType &step_alg,
343  KUKA::FRI::ClientData &friData,
344  boost::system::error_code &receive_ec,
345  std::size_t &receive_bytes_transferred,
346  boost::system::error_code &send_ec,
347  std::size_t &send_bytes_transferred,
348  boost::asio::ip::udp::endpoint sender_endpoint =
349  boost::asio::ip::udp::endpoint()) {
350 
351  static const int message_flags = 0;
352  receive_bytes_transferred = socket.receive_from(
353  boost::asio::buffer(friData.receiveBuffer,
354  KUKA::FRI::FRI_MONITOR_MSG_MAX_SIZE),
355  sender_endpoint, message_flags, receive_ec);
356  decode(friData, receive_bytes_transferred);
357 
358  friData.lastSendCounter++;
359  // Check whether to send a response
360  if (friData.lastSendCounter >=
361  friData.monitoringMsg.connectionInfo.receiveMultiplier) {
362  send_bytes_transferred = encode(step_alg, friData, send_ec);
363  if (send_ec)
364  return;
365  socket.send(boost::asio::buffer(friData.sendBuffer, send_bytes_transferred),
366  message_flags, send_ec);
367  }
368 }
369 
370 
371 /// @brief don't use this
372 /// @deprecated this is an old implemenation that will be removed in the future
373 void copy(const FRIMonitoringMessage &monitoringMsg, KukaState &state) {
374  state.clear();
375  copy(monitoringMsg, std::back_inserter(state.position),
377  copy(monitoringMsg, std::back_inserter(state.torque),
379  copy(monitoringMsg, std::back_inserter(state.commandedPosition),
381  copy(monitoringMsg, std::back_inserter(state.commandedTorque),
383  copy(monitoringMsg, std::back_inserter(state.ipoJointPosition),
385  state.sessionState = static_cast<flatbuffer::ESessionState>(
386  get(monitoringMsg, KUKA::FRI::ESessionState()));
388  get(monitoringMsg, KUKA::FRI::EConnectionQuality()));
389  state.safetyState = static_cast<flatbuffer::ESafetyState>(
390  get(monitoringMsg, KUKA::FRI::ESafetyState()));
391  state.operationMode = static_cast<flatbuffer::EOperationMode>(
392  get(monitoringMsg, KUKA::FRI::EOperationMode()));
393  state.driveState = static_cast<flatbuffer::EDriveState>(
394  get(monitoringMsg, KUKA::FRI::EDriveState()));
395 
396  /// @todo fill out missing state update steps
397 }
398 
399 /// @brief Simple low level driver to communicate over the Kuka iiwa FRI
400 /// interface using KUKA::FRI::ClientData status objects
401 ///
402 /// @note If you aren't sure, see KukaDriver in KukaDriver.hpp.
403 ///
404 /// @note If you want to change how the lowest level high rate updates are
405 /// performed see KukaFRIdriver
406 ///
407 /// One important aspect of this design is the is_running_automatically flag. If
408 /// you are unsure,
409 /// the suggested default is run_automatically (true/enabled). When it is
410 /// enabled,
411 /// the driver will create a thread internally and run the event loop
412 /// (io_service) itself.
413 /// If run manually, you are expected to call io_service.run() on the io_service
414 /// you provide,
415 /// or on the run() member function. When running manually you are also expected
416 /// to call
417 /// async_getLatestState(handler) frequently enought that the 5ms response
418 /// requirement of the KUKA
419 /// FRI interface is met.
420 template <typename LowLevelStepAlgorithmType = LinearInterpolation>
422  : public std::enable_shared_from_this<
423  KukaFRIClientDataDriver<LowLevelStepAlgorithmType>>,
424  public KukaUDP {
425 
426 public:
427  using KukaUDP::ParamIndex;
429  using KukaUDP::Params;
431 
432  KukaFRIClientDataDriver(boost::asio::io_service &ios,
433  Params params = defaultParams())
434  : params_(params), m_shouldStop(false), isConnectionEstablished_(false),
435  io_service_(ios)
436  //,socketP_(std::move(connect(params, io_service_,sender_endpoint_))) ///<
437  //@todo this breaks the assumption that the object can be constructed without
438  // hardware issues being a porblem
439  {
440  construct(params);
441  }
442 
443  KukaFRIClientDataDriver(Params params = defaultParams())
444  : params_(params), m_shouldStop(false), isConnectionEstablished_(false),
445  optional_internal_io_service_P(new boost::asio::io_service),
446  io_service_(*optional_internal_io_service_P)
447  //,socketP_(std::move(connect(params, io_service_,sender_endpoint_))) ///<
448  //@todo this breaks the assumption that the object can be constructed without
449  // hardware issues being a porblem
450  {
451  construct(params);
452  }
453 
454  /// Call this to initialize the object after the constructor has been called
455  void construct(Params params = defaultParams()) {
456  try {
457 
458  ///////////
459  // initialize all of the states
460  latestStateForUser_ = make_valid_LatestState();
461  spareStates_.emplace_back(std::move(make_valid_LatestState()));
462  spareStates_.emplace_back(std::move(make_valid_LatestState()));
463 
464  // start up the driver thread since the io_service_ is internal only
465  if (std::get<is_running_automatically>(params)) {
466  driver_threadP_.reset(new std::thread([&] { update(); }));
467  }
468 
469  } catch (boost::exception &e) {
470  add_details_to_connection_error(e, params);
471  throw;
472  }
473  }
474 
475  /// @brief blocking call to communicate with the robot continuously
476  /// @pre construct() should be called before run()
477  void run() { update(); }
478 
479  /// @brief Updates the passed friData shared pointer to a pointer to newly
480  /// updated data, plus any errors that occurred.
481  ///
482  /// We recommend you supply a valid shared_ptr to friData, even if all command
483  /// elements are set to false.
484  /// The friData pointer you pass in can contain a command to send to the arm.
485  /// To update with new state and optional input state, you give up lifetime
486  /// control of the input,
487  /// and assume liftime control of the output.
488  ///
489  /// This function is designed for single threaded use to quickly receive and
490  /// send "non-blocking" updates to the robot.
491  /// It is not thread safe cannot be called simultaneously from multiple
492  /// threads.
493  ///
494  ///
495  /// @note { An error code is set if update_state is called with no new data
496  /// available.
497  /// In this special case, all error codes and bytes_transferred are 0,
498  /// because
499  /// there was no new data available for the user.
500  /// }
501  ///
502  /// @warning Do not pound this call continuously in a very tight loop, because
503  /// then the driver won't be able to acquire the lock and send updates to the
504  /// robot.
505  ///
506  /// @param[in,out] friData supply a new command, receive a new update of the
507  /// robot state. Pointer is null if no new data is available.
508  ///
509  /// @pre If friData!=nullptr it is assumed valid for use and this class will
510  /// take control of the object.
511  ///
512  /// @return isError = false if you have new data, true when there is either an
513  /// error or no new data
514  bool update_state(LowLevelStepAlgorithmType &step_alg,
515  std::shared_ptr<KUKA::FRI::ClientData> &friData,
516  boost::system::error_code &receive_ec,
517  std::size_t &receive_bytes_transferred,
518  boost::system::error_code &send_ec,
519  std::size_t &send_bytes_transferred) {
520 
521  if (exceptionPtr) {
522  /// @note this exception most likely came from the update() call running
523  /// the kuka driver
524  std::rethrow_exception(exceptionPtr);
525  }
526 
527  bool haveNewData = false;
528 
529  if (!isConnectionEstablished_ ||
530  !std::get<latest_receive_monitor_state>(latestStateForUser_)) {
531  // no new data, so immediately return results accordingly
532  std::tie(friData, receive_ec, receive_bytes_transferred, send_ec,
533  send_bytes_transferred) = make_LatestState(friData);
534  return !haveNewData;
535  }
536 
537  // ensure we have valid data for future updates
538  // need to copy this over because friData will be set as an output value
539  // later
540  // and allocate/initialize data if null
541  auto validFriDataLatestState = make_valid_LatestState(friData);
542 
543  // get the latest state from the driver thread
544  {
545  boost::lock_guard<boost::mutex> lock(ptrMutex_);
546 
547  // update the stepping algorithm
548  /// @todo is this thread safe?
549  step_alg_ = step_alg;
550 
551  // get the update if one is available
552  // the user has provided new data to send to the device
553  if (std::get<latest_receive_monitor_state>(validFriDataLatestState)
554  ->commandMsg.has_commandData) {
555  std::swap(validFriDataLatestState, newCommandForDriver_);
556  }
557  // newCommandForDriver_ is finalized
558 
559  if (spareStates_.size() < spareStates_.capacity() &&
560  std::get<latest_receive_monitor_state>(validFriDataLatestState)) {
561  spareStates_.emplace_back(std::move(validFriDataLatestState));
562  }
563 
564  if (std::get<latest_receive_monitor_state>(latestStateForUser_)) {
565  // return the latest state to the caller
566  std::tie(friData, receive_ec, receive_bytes_transferred, send_ec,
567  send_bytes_transferred) = std::move(latestStateForUser_);
568  haveNewData = true;
569  } else if (std::get<latest_receive_monitor_state>(
570  validFriDataLatestState)) {
571  // all storage is full, return the spare data to the user
572  std::tie(friData, receive_ec, receive_bytes_transferred, send_ec,
573  send_bytes_transferred) = validFriDataLatestState;
574  }
575  }
576 
577  // let the user know if we aren't in the best possible state
578  return !haveNewData || receive_bytes_transferred == 0 || receive_ec ||
579  send_ec;
580  }
581 
582  void destruct() {
583  m_shouldStop = true;
584  if (driver_threadP_) {
585  driver_threadP_->join();
586  }
587  }
588 
589  ~KukaFRIClientDataDriver() { destruct(); }
590 
591  /// Is everything ok?
592  /// @return true if the kuka fri connection is actively running without any
593  /// issues
594  /// @todo consider expanding to support real error codes
595  bool is_active() { return !exceptionPtr && isConnectionEstablished_; }
596 
597 private:
598  /// Reads data off of the real kuka fri device in a separate thread
599  /// @todo consider switching to single producer single consumer queue to avoid
600  /// locking overhead, but keep latency in mind
601  /// https://github.com/facebook/folly/blob/master/folly/docs/ProducerConsumerQueue.md
602  void update() {
603  try {
604 
605  /// nextState is the object currently being loaded with data off the
606  /// network
607  /// the driver thread should access this exclusively in update()
608  LatestState nextState = make_valid_LatestState();
609  LatestState latestCommandBackup = make_valid_LatestState();
610 
611  boost::asio::ip::udp::endpoint sender_endpoint;
612  boost::asio::ip::udp::socket socket(
613  connect(params_, io_service_, sender_endpoint));
614  KukaState kukastate; ///< @todo remove this line when new api works
615  /// completely since old one is deprecated
616 
617  /////////////
618  // run the primary update loop in a separate thread
619  while (!m_shouldStop) {
620  /// @todo maybe there is a more convienient way to set this that is
621  /// easier for users? perhaps initializeClientDataForiiwa()?
622 
623  // nextState and latestCommandBackup should never be null
624  BOOST_VERIFY(std::get<latest_receive_monitor_state>(nextState));
625  BOOST_VERIFY(
626  std::get<latest_receive_monitor_state>(latestCommandBackup));
627 
628  // set the flag that must always be there
629  std::get<latest_receive_monitor_state>(nextState)
630  ->expectedMonitorMsgID = KUKA::LBRState::LBRMONITORMESSAGEID;
631 
632  // actually talk over the network to receive an update and send out a
633  // new command
635  socket, step_alg_,
636  *std::get<latest_receive_monitor_state>(nextState),
637  std::get<latest_receive_ec>(nextState),
638  std::get<latest_receive_bytes_transferred>(nextState),
639  std::get<latest_send_ec>(nextState),
640  std::get<latest_send_bytes_transferred>(nextState));
641 
642  /// @todo use atomics to eliminate the global mutex lock for this object
643  // lock the mutex to communicate with the user thread
644  // if it cannot lock, simply send the previous message
645  // again
646  if (ptrMutex_.try_lock()) {
647 
648  //////////////////////////////////////////////
649  // right now this is the state of everything:
650  //////////////////////////////////////////////
651  //
652  // Always Valid:
653  //
654  // nextState: valid, contains the latest update
655  // latestCommandBackup: should always be valid (though hasCommand
656  // might be false)
657  //
658  // Either Valid or Null:
659  // latestStateForUser_ : null if the user took data out, valid
660  // otherwise
661  // newCommandForDriver_: null if there is no new command data from
662  // the user, vaild otherwise
663 
664  // 1) set the outgoing latest state for the user to pick up
665  // latestStateForUser_ is finalized
666  std::swap(latestStateForUser_, nextState);
667 
668  // 2) get a new incoming command if available and set incoming command
669  // variable to null
670  if (std::get<latest_receive_monitor_state>(newCommandForDriver_)) {
671  // 3) back up the new incoming command
672  // latestCommandBackup is finalized, newCommandForDriver_ needs to
673  // be cleared out
674  std::swap(latestCommandBackup, newCommandForDriver_);
675 
676  // nextState may be null
677  if (!std::get<latest_receive_monitor_state>(nextState)) {
678  nextState = std::move(newCommandForDriver_);
679  } else if (!(spareStates_.size() == spareStates_.capacity())) {
680  spareStates_.emplace_back(std::move(newCommandForDriver_));
681  } else {
682  std::get<latest_receive_monitor_state>(newCommandForDriver_)
683  .reset();
684  }
685  }
686 
687  // finalized: latestStateForUser_, latestCommandBackup,
688  // newCommandForDriver_ is definitely null
689  // issues to be resolved:
690  // nextState: may be null right now, and it should be valid
691  // newCommandForDriver_: needs to be cleared with 100% certainty
692  BOOST_VERIFY(spareStates_.size() > 0);
693 
694  if (!std::get<latest_receive_monitor_state>(nextState) &&
695  spareStates_.size()) {
696  // move the last element out and shorten the vector
697  nextState = std::move(*(spareStates_.end() - 1));
698  spareStates_.pop_back();
699  }
700 
701  BOOST_VERIFY(std::get<latest_receive_monitor_state>(nextState));
702 
703  KUKA::FRI::ClientData &nextClientData =
704  *std::get<latest_receive_monitor_state>(nextState);
705  KUKA::FRI::ClientData &latestClientData =
706  *std::get<latest_receive_monitor_state>(latestStateForUser_);
707 
708  // copy essential data from latestStateForUser_ to nextState
709  nextClientData.lastState = latestClientData.lastState;
710  nextClientData.sequenceCounter = latestClientData.sequenceCounter;
711  nextClientData.lastSendCounter = latestClientData.lastSendCounter;
712  nextClientData.expectedMonitorMsgID =
713  latestClientData.expectedMonitorMsgID;
714 
715  // copy command from latestCommandBackup to nextState aka
716  // nextClientData
717  KUKA::FRI::ClientData &latestCommandBackupClientData =
718  *std::get<latest_receive_monitor_state>(latestCommandBackup);
719  set(nextClientData.commandMsg,
720  latestCommandBackupClientData.commandMsg);
721 
722  // if there are no error codes and we have received data,
723  // then we can consider the connection established!
724  /// @todo perhaps data should always send too?
725  if (!std::get<latest_receive_ec>(nextState) &&
726  !std::get<latest_send_ec>(nextState) &&
727  std::get<latest_receive_bytes_transferred>(nextState)) {
728  isConnectionEstablished_ = true;
729  }
730 
731  ptrMutex_.unlock();
732  }
733  }
734 
735  } catch (...) {
736  // transport the exception to the main thread in a safe manner
737  exceptionPtr = std::current_exception();
738  m_shouldStop = true;
739  isConnectionEstablished_ = false;
740  }
741 
742  isConnectionEstablished_ = false;
743  }
744 
745  enum LatestStateIndex {
746  latest_receive_monitor_state,
747  latest_receive_ec,
748  latest_receive_bytes_transferred,
749  latest_send_ec,
750  latest_send_bytes_transferred
751  };
752 
753  typedef std::tuple<std::shared_ptr<KUKA::FRI::ClientData>,
754  boost::system::error_code, std::size_t,
755  boost::system::error_code, std::size_t>
756  LatestState;
757 
758  /// Creates a default LatestState Object
759  static LatestState
760  make_LatestState(std::shared_ptr<KUKA::FRI::ClientData> &clientData) {
761  return std::make_tuple(clientData, boost::system::error_code(),
762  std::size_t(), boost::system::error_code(),
763  std::size_t());
764  }
765 
766  /// creates a shared_ptr to KUKA::FRI::ClientData with all command message
767  /// status explicitly set to false
768  /// @post std::shared_ptr<KUKA::FRI::ClientData> will be non-null
769  static std::shared_ptr<KUKA::FRI::ClientData> make_shared_valid_ClientData(
770  std::shared_ptr<KUKA::FRI::ClientData> &friData) {
771  if (friData.get() == nullptr) {
772  friData =
773  std::make_shared<KUKA::FRI::ClientData>(KUKA::LBRState::NUM_DOF);
774  // there is no commandMessage data on a new object
775  friData->resetCommandMessage();
776  }
777 
778  return friData;
779  }
780 
781  static std::shared_ptr<KUKA::FRI::ClientData> make_shared_valid_ClientData() {
782  std::shared_ptr<KUKA::FRI::ClientData> friData;
783  return make_shared_valid_ClientData(friData);
784  }
785 
786  /// Initialize valid shared ptr to LatestState object with a valid allocated
787  /// friData
788  static LatestState
789  make_valid_LatestState(std::shared_ptr<KUKA::FRI::ClientData> &friData) {
790  if (!friData)
791  friData = make_shared_valid_ClientData();
792 
793  return make_LatestState(friData);
794  }
795 
796  static LatestState make_valid_LatestState() {
797  std::shared_ptr<KUKA::FRI::ClientData> friData;
798  return make_valid_LatestState(friData);
799  }
800 
801  Params params_;
802 
803  /// @todo replace with unique_ptr
804  /// the latest state we have available to give to the user
805  LatestState latestStateForUser_;
806  LatestState newCommandForDriver_;
807 
808  /// should always be valid, never null
809  boost::container::static_vector<LatestState, 2> spareStates_;
810 
811  std::atomic<bool> m_shouldStop;
812  std::exception_ptr exceptionPtr;
813  std::atomic<bool> isConnectionEstablished_;
814 
815  /// may be null, allows the user to choose if they want to provide an
816  /// io_service
817  std::unique_ptr<boost::asio::io_service> optional_internal_io_service_P;
818 
819  // other things to do somewhere:
820  // - get list of control points
821  // - get the control point in the arm base coordinate system
822  // - load up a configuration file with ip address to send to, etc.
823  boost::asio::io_service &io_service_;
824  std::unique_ptr<std::thread> driver_threadP_;
825  boost::mutex ptrMutex_;
826 
827  LowLevelStepAlgorithmType step_alg_;
828 };
829 
830 /// @brief Primary Kuka FRI driver, only talks over realtime network FRI KONI
831 /// ethernet port
832 ///
833 ///
834 /// @note If you aren't sure, see KukaDriver in KukaDriver.hpp.
835 ///
836 /// @note If you want to change how the lowest level high rate updates are
837 /// performed, make another version of this class. @see KukaFRIdriver
838 ///
839 ///
840 /// KukaFRIdriver is a low level driver at a slightly "higher level" than the
841 /// the "lowest level" KukaFRIClientDataDriver
842 /// to communicate. This is the class you will want to replace if you want to
843 /// change how low level position
844 /// updates are changed between FRI update steps, which occur at a configurable
845 /// duration of 1-5 ms.
846 ///
847 /// For position based motion to work, you must set both the position you want
848 /// and the time you want it to get there.
849 /// This is required because the robot can move extremely fast, so accidentally
850 /// setting the velocity to the max is
851 /// very dangerous. If the time point is in the past, the robot will not move.
852 /// If the time point is too near in the future
853 /// to make it, the robot will move at the max speed towards that position.
854 ///
855 ///
856 /// While velocity limits are not provided explicitly by KUKA in their low level
857 /// C++ API,
858 /// if you send a command that violates the velocity limits specified in KUKA's
859 /// documenation
860 /// the arm stops immediately with an error, even over FRI.
861 ///
862 /// @todo support generic read/write
863 /// @todo ensure commands stay within machine limits to avoid lockup
864 /// @todo reset and resume if lockup occurs whenever possible
865 /// @todo in classes that use this driver, make the use of this class templated
866 /// so that the low level update algorithm can change.
867 /// @todo add getter for number of milliseconds between fri updates (1-5) aka
868 /// sync_period aka send_period aka ms per tick
869 template <typename LowLevelStepAlgorithmType = LinearInterpolation>
870 class KukaFRIdriver : public std::enable_shared_from_this<
871  KukaFRIdriver<LowLevelStepAlgorithmType>>,
872  public KukaUDP {
873 
874 public:
875  using KukaUDP::ParamIndex;
877  using KukaUDP::Params;
879 
880  KukaFRIdriver(Params params = defaultParams()) : params_(params) {}
881 
882  // KukaFRIdriver(boost::asio::io_service&
883  // device_driver_io_service__,Params params = defaultParams())
884  // :
885  // device_driver_io_service(device_driver_io_service__),
886  // params_(params)
887  // {}
888 
889  void construct() { construct(params_); }
890 
891  /// @todo create a function that calls simGetObjectHandle and throws an
892  /// exception when it fails
893  /// @warning getting the ik group is optional, so it does not throw an
894  /// exception
895  void construct(Params params) {
896 
897  params_ = params;
898  // keep driver threads from exiting immediately after creation, because they
899  // have work to do!
900  device_driver_workP_.reset(
901  new boost::asio::io_service::work(device_driver_io_service));
902 
903  kukaFRIClientDataDriverP_.reset(
905  device_driver_io_service,
906  std::make_tuple(std::string(std::get<RobotModel>(params)),
907  std::string(std::get<localhost>(params)),
908  std::string(std::get<localport>(params)),
909  std::string(std::get<remotehost>(params)),
910  std::string(std::get<remoteport>(params)),
912  LowLevelStepAlgorithmType>::run_automatically))
913 
914  );
915  }
916 
917  const Params &getParams() { return params_; }
918 
920  device_driver_workP_.reset();
921 
922  if (driver_threadP) {
923  device_driver_io_service.stop();
924  driver_threadP->join();
925  }
926  }
927 
928  /// gets the number of seconds in one message exchange "tick" aka "cycle",
929  /// "time step"
930  double getSecondsPerTick() {
931  return std::chrono::duration_cast<std::chrono::seconds>(
932  std::chrono::milliseconds(grl::robot::arm::get(
933  friData_->monitoringMsg, grl::time_step_tag())))
934  .count();
935  }
936 
937  /// @todo make this configurable for different specific robots. Currently set
938  /// for kuka lbr iiwa 14kg R820
940  KukaState::joint_state maxVel;
941  /// get max velocity constraint parameter for this robot model
942  copy(std::get<RobotModel>(params_), std::back_inserter(maxVel),
944 
945  // scale velocity down to a single timestep. In other words multiply each
946  // velocity by the number of seconds in a tick, likely 0.001-0.005
947  boost::transform(
948  maxVel, maxVel.begin(),
949  std::bind2nd(std::multiplies<KukaState::joint_state::value_type>(),
950  getSecondsPerTick()));
951 
952  return maxVel;
953  }
954 
955  /**
956  * spin once, this is what you call each time you synchronize the client with
957  * the robot over UDP
958  * it is expected to be called at least once per send_period_millisec, which
959  * is the time between
960  * each FRI udp packet.
961  *
962  */
963  bool run_one() {
964  // note: this one sends *and* receives the joint data!
965  BOOST_VERIFY(kukaFRIClientDataDriverP_.get() != nullptr);
966  /// @todo use runtime calculation of NUM_JOINTS instead of constant
967  if (!friData_)
968  friData_ =
969  std::make_shared<KUKA::FRI::ClientData>(KUKA::LBRState::NUM_DOF);
970 
971  bool haveNewData = false;
972 
973  static const std::size_t minimumConsecutiveSuccessesBeforeSendingCommands =
974  100;
975 
976  std::unique_ptr<LinearInterpolation> lowLevelStepAlgorithmP;
977 
978  /// @todo probably only need to set this once
979  armState.velocity_limits.clear();
980  armState.velocity_limits = getMaxVel();
981 
982  // This is the key point where the arm's motion goal command is updated and
983  // sent to the robot
984  // Set the FRI to the simulated joint positions
985  if (this->m_haveReceivedRealDataCount >
986  minimumConsecutiveSuccessesBeforeSendingCommands) {
987  boost::lock_guard<boost::mutex> lock(jt_mutex);
988  lowLevelStepAlgorithmP.reset(new LinearInterpolation(armState));
989  /// @todo construct new low level command object and pass to
990  /// KukaFRIClientDataDriver
991  /// this is where we used to setup a new FRI command
992 
993  // std::cout << "commandToSend: " << commandToSend << "\n" <<
994  // "currentJointPos: " << currentJointPos << "\n" << "amountToMove: " <<
995  // amountToMove << "\n" << "maxVel: " << maxvel << "\n";
996  } else {
997  KukaState tmp;
998  tmp.velocity_limits = getMaxVel();
999  lowLevelStepAlgorithmP.reset(new LinearInterpolation(tmp));
1000  }
1001 
1002  BOOST_VERIFY(lowLevelStepAlgorithmP != nullptr);
1003  boost::system::error_code send_ec, recv_ec;
1004  std::size_t send_bytes, recv_bytes;
1005  // sync with device over network
1006  haveNewData = !kukaFRIClientDataDriverP_->update_state(
1007  *lowLevelStepAlgorithmP, friData_, recv_ec, recv_bytes, send_ec,
1008  send_bytes);
1009  m_attemptedCommunicationCount++;
1010 
1011  if (haveNewData) {
1012  boost::lock_guard<boost::mutex> lock(jt_mutex);
1013  // if there were problems sending commands, start by sending the current
1014  // position
1015  // if(this->m_haveReceivedRealDataCount >
1016  // minimumConsecutiveSuccessesBeforeSendingCommands-1)
1017  // {
1018  // boost::lock_guard<boost::mutex> lock(jt_mutex);
1019  // // initialize arm commands to current arm position
1020  // armState.clearCommands();
1021  //// armState.commandedPosition.clear();
1022  //// armState.commandedTorque.clear();
1023  //// grl::robot::arm::copy(friData_->monitoringMsg,
1024  /// std::back_inserter(armState.commandedPosition),
1025  /// grl::revolute_joint_angle_open_chain_command_tag());
1026  //// grl::robot::arm::copy(friData_->monitoringMsg,
1027  /// std::back_inserter(armState.commandedTorque) ,
1028  /// grl::revolute_joint_torque_open_chain_command_tag());
1029  // }
1030 
1031  m_attemptedCommunicationConsecutiveSuccessCount++;
1032  this->m_attemptedCommunicationConsecutiveFailureCount = 0;
1033  this->m_haveReceivedRealDataCount++;
1034 
1035  // We have the real kuka state read from the device now
1036  // update real joint angle data
1037  armState.position.clear();
1038  grl::robot::arm::copy(friData_->monitoringMsg,
1039  std::back_inserter(armState.position),
1041 
1042  armState.torque.clear();
1043  grl::robot::arm::copy(friData_->monitoringMsg,
1044  std::back_inserter(armState.torque),
1046 
1047  armState.externalTorque.clear();
1049  friData_->monitoringMsg, std::back_inserter(armState.externalTorque),
1051 
1052  armState.externalForce.clear();
1053  grl::robot::arm::copy(friData_->monitoringMsg,
1054  std::back_inserter(armState.externalForce),
1056 
1057  armState.ipoJointPosition.clear();
1059  friData_->monitoringMsg,
1060  std::back_inserter(armState.ipoJointPosition),
1062 
1063  armState.sendPeriod = std::chrono::milliseconds(
1064  grl::robot::arm::get(friData_->monitoringMsg, grl::time_step_tag()));
1065 
1066  // std::cout << "Measured Torque: ";
1067  // std::cout << std::setw(6);
1068  // for (float t:armState.torque) {
1069  // std::cout << t << " ";
1070  // }
1071  // std::cout << '\n';
1072  //
1073  // std::cout << "External Torque: ";
1074  // std::cout << std::setw(6);
1075  // for (float t:armState.externalTorque) {
1076  // std::cout << t << " ";
1077  // }
1078  // std::cout << '\n';
1079  //
1080  // std::cout << "External Force: ";
1081  // for (float t:armState.externalForce) {
1082  // std::cout << t << " ";
1083  // }
1084  // std::cout << '\n';
1085 
1086  } else {
1087  m_attemptedCommunicationConsecutiveFailureCount++;
1088  std::cerr << "No new FRI data available, is an FRI application running "
1089  "on the Kuka arm? \n Total sucessful transfers: "
1090  << this->m_haveReceivedRealDataCount
1091  << "\n Total attempts: " << m_attemptedCommunicationCount
1092  << "\n Consecutive Failures: "
1093  << m_attemptedCommunicationConsecutiveFailureCount
1094  << "\n Consecutive Successes: "
1095  << m_attemptedCommunicationConsecutiveSuccessCount << "\n";
1096  m_attemptedCommunicationConsecutiveSuccessCount = 0;
1097  /// @todo should the results of getlatest state even be possible to call
1098  /// without receiving real data? should the library change?
1099  }
1100 
1101  return haveNewData;
1102  }
1103 
1104  /**
1105  * \brief Set the joint positions for the current interpolation step.
1106  *
1107  * This method is only effective when the robot is in a commanding state
1108  * and the set time point for reaching the destination is in the future.
1109  * This function sets the goal time point for a motion to the epoch, aka "time
1110  * 0" (which is in the past) for safety.
1111  *
1112  *
1113  * For position based motion to work, you must set both the position you want
1114  * and the time you want it to get there.
1115  * This is required because the robot can move extremely fast, so accidentally
1116  * setting the velocity to the max is
1117  * very dangerous. If the time point is in the past, the robot will not move.
1118  * If the time point is too near in the future
1119  * to make it, the robot will move at the max speed towards that position.
1120  *
1121  * @see KukaFRIdriver::set(TimePoint && time, time_point_command_tag) to set
1122  * the destination time point in the future so the position motion can start.
1123  *
1124  * @param state Object which stores the current state of the robot, including
1125  * the command to send next
1126  * @param range Array with the new joint positions (in radians)
1127  * @param tag identifier object indicating that revolute joint angle commands
1128  * should be modified
1129  */
1130  template <typename Range>
1132  boost::lock_guard<boost::mutex> lock(jt_mutex);
1133  armState.clearCommands();
1134  boost::copy(range, std::back_inserter(armState.commandedPosition));
1135  boost::copy(range, std::back_inserter(armState.commandedPosition_goal));
1136  }
1137 
1138  /**
1139  * @brief Set the time duration expected between new position commands in ms
1140  *
1141  * The driver will likely be updated every so often, such as every 25ms, and
1142  * the lowest level of the
1143  * driver may update even more frequently, such as every 1ms. By providing as
1144  * accurate an
1145  * estimate between high level updates the low level driver can smooth out the
1146  * motion through
1147  * interpolation (the default), or another algorithm. See
1148  * LowLevelStepAlgorithmType template parameter
1149  * in the KukaFRIdriver class if you want to change out the low level
1150  * algorithm.
1151  *
1152  * @see KukaFRIdriver::get(time_duration_command_tag)
1153  *
1154  * @param duration_to_goal_command std::chrono time format representing the
1155  * time duration between updates
1156  *
1157  */
1158  void set(double duration_to_goal_command, time_duration_command_tag) {
1159  boost::lock_guard<boost::mutex> lock(jt_mutex);
1160  armState.goal_position_command_time_duration = duration_to_goal_command;
1161  }
1162 
1163  /**
1164  * @brief Get the timestamp of the most recent armState
1165  *
1166  *
1167  *
1168  * @see KukaFRIdriver::set(Range&& range,
1169  * grl::revolute_joint_angle_open_chain_command_tag)
1170  *
1171  */
1173  boost::lock_guard<boost::mutex> lock(jt_mutex);
1174  return armState.timestamp;
1175  }
1176 
1177  /**
1178  * \brief Set the applied joint torques for the current interpolation step.
1179  *
1180  * This method is only effective when the client is in a commanding state.
1181  * The ControlMode of the robot has to be joint impedance control mode. The
1182  * Client Command Mode has to be torque.
1183  *
1184  * @param state Object which stores the current state of the robot, including
1185  * the command to send next
1186  * @param torques Array with the applied torque values (in Nm)
1187  * @param tag identifier object indicating that the torqe value command should
1188  * be modified
1189  */
1190  template <typename Range>
1192  boost::lock_guard<boost::mutex> lock(jt_mutex);
1193  armState.clearCommands();
1194  boost::copy(range, armState.commandedTorque);
1195  }
1196 
1197  /**
1198  * \brief Set the applied wrench vector of the current interpolation step.
1199  *
1200  * The wrench vector consists of:
1201  * [F_x, F_y, F_z, tau_A, tau_B, tau_C]
1202  *
1203  * F ... forces (in N) applied along the Cartesian axes of the
1204  * currently used motion center.
1205  * tau ... torques (in Nm) applied along the orientation angles
1206  * (Euler angles A, B, C) of the currently used motion center.
1207  *
1208  * This method is only effective when the client is in a commanding state.
1209  * The ControlMode of the robot has to be Cartesian impedance control mode.
1210  * The
1211  * Client Command Mode has to be wrench.
1212  *
1213  * @param state object storing the command data that will be sent to the
1214  * physical device
1215  * @param range wrench Applied Cartesian wrench vector, in x, y, z, roll,
1216  * pitch, yaw force measurments.
1217  * @param tag identifier object indicating that the wrench value command
1218  * should be modified
1219  *
1220  * @todo perhaps support some specific more useful data layouts
1221  */
1222  template <typename Range>
1223  void set(Range &&range, grl::cartesian_wrench_command_tag) {
1224  boost::lock_guard<boost::mutex> lock(jt_mutex);
1225  armState.clearCommands();
1226  std::copy(range, armState.commandedCartesianWrenchFeedForward);
1227  }
1228 
1229  /// @todo should this exist? is it written correctly?
1230  void get(KukaState &state) {
1231  boost::lock_guard<boost::mutex> lock(jt_mutex);
1232  state = armState;
1233  }
1234 
1235  // The total number of times the FRI interface has successfully received a UDP
1236  // packet
1237  // from the robot since this class was initialized.
1238  volatile std::size_t m_haveReceivedRealDataCount = 0;
1239  // The total number of times the FRI interface has attempted to receive a UDP
1240  // packet
1241  // from the robot since this class was initialized, regardless of if it was
1242  // successful or not.
1243  volatile std::size_t m_attemptedCommunicationCount = 0;
1244  // The number of consecutive FRI receive calls that have failed to get data
1245  // successfully, resets to 0 on a single success.
1246  volatile std::size_t m_attemptedCommunicationConsecutiveFailureCount = 0;
1247  // The number of consecutive FRI receive calls that have received data
1248  // successfully, resets to 0 on a single failure.
1249  volatile std::size_t m_attemptedCommunicationConsecutiveSuccessCount = 0;
1250 
1251  boost::asio::io_service device_driver_io_service;
1252  std::unique_ptr<boost::asio::io_service::work> device_driver_workP_;
1253  std::unique_ptr<std::thread> driver_threadP;
1254  std::shared_ptr<
1257 
1258 private:
1259  KukaState armState;
1260  boost::mutex jt_mutex;
1261 
1262  Params params_;
1263  std::shared_ptr<KUKA::FRI::ClientData> friData_;
1264 };
1265 
1266 /// @brief nonmember wrapper function to help integrate KukaFRIdriver objects
1267 /// with generic programming interface
1268 template <typename LowLevelStepAlgorithmType = LinearInterpolation,
1269  typename Range, typename T>
1270 static inline void set(KukaFRIdriver<LowLevelStepAlgorithmType> &state,
1271  Range &&range, T t) {
1272  state.set(range, t);
1273 }
1274 }
1275 }
1276 } // namespace grl::robot::arm
1277 
1278 #endif
joint_state velocity_limits
Definition: Kuka.hpp:97
void decode(KUKA::FRI::ClientData &friData, std::size_t msg_size)
internal function to decode KUKA FRI message buffer (using nanopb decoder) for the KUKA FRI ...
void copy(const FRIMonitoringMessage &monitoringMsg, KukaState &state)
don&#39;t use this
std::tuple< std::string, std::string, std::string, std::string, std::string, ThreadingRunMode > Params
Definition: Kuka.hpp:207
joint velocity constraint (ex: max velocity)
Definition: tags.hpp:58
void operator()(ArmData &friData, revolute_joint_torque_open_chain_command_tag)
flatbuffer::ESafetyState safetyState
Definition: Kuka.hpp:67
joint_state commandedPosition
Definition: Kuka.hpp:51
std::ostream & operator<<(std::ostream &out, const boost::container::static_vector< T, N > &v)
void operator()(ArmDataType &, CommandModeType &)
boost::asio::io_service device_driver_io_service
Default LowLevelStepAlgorithmType This algorithm is designed to be changed out.
Internal implementation class for driver use only, stores all the kuka state data in a simple object...
Definition: Kuka.hpp:40
std::chrono::time_point< std::chrono::high_resolution_clock > time_point_type
Definition: Kuka.hpp:45
std::unique_ptr< boost::asio::io_service::work > device_driver_workP_
KukaFRIClientDataDriver(Params params=defaultParams())
Simple low level driver to communicate over the Kuka iiwa FRI interface using KUKA::FRI::ClientData s...
KUKA::FRI::ESafetyState get(const FRIMonitoringMessage &monitoringMsg, const KUKA::FRI::ESafetyState)
external joint torque, i.e. torques applied to an arm excluding those caused by the mass of the arm i...
Definition: tags.hpp:64
cartesian_state commandedCartesianWrenchFeedForward
Definition: Kuka.hpp:52
Primary Kuka FRI driver, only talks over realtime network FRI KONI ethernet port. ...
OutputIterator copy(std::string model, OutputIterator it, grl::revolute_joint_velocity_open_chain_state_constraint_tag)
copy vector of joint velocity limits in radians/s
Definition: Kuka.hpp:131
const int NUM_DOF
Definition: Kuka.hpp:19
joint_state ipoJointPosition
Definition: Kuka.hpp:56
void construct(Params params=defaultParams())
Call this to initialize the object after the constructor has been called.
std::unique_ptr< std::thread > driver_threadP
flatbuffer::EOperationMode operationMode
Definition: Kuka.hpp:68
std::shared_ptr< grl::robot::arm::KukaFRIClientDataDriver< LowLevelStepAlgorithmType > > kukaFRIClientDataDriverP_
the time duration over which a command should be completed
Definition: tags.hpp:73
joint_state position
Definition: Kuka.hpp:47
KukaState::joint_state getMaxVel()
joint_state commandedPosition_goal
we need to mind arm constraints, so we set a goal then work towards it
Definition: Kuka.hpp:82
const int LBRMONITORMESSAGEID
Definition: Kuka.hpp:20
void operator()(ArmData &friData, cartesian_wrench_command_tag)
void run()
blocking call to communicate with the robot continuously
joint_state torque
Definition: Kuka.hpp:48
flatbuffer::EDriveState driveState
Definition: Kuka.hpp:69
LinearInterpolation(const KukaState &armState_)
std::size_t encode(LowLevelStepAlgorithmType &step_alg, KUKA::FRI::ClientData &friData, boost::system::error_code &ec)
encode data in the class KUKA::FRI::ClientData into the send buffer for the KUKA FRI. this preps the information for transport over the network
std::tuple< std::string, std::string, std::string, std::string, std::string, ThreadingRunMode > Params
Definition: Kuka.hpp:207
bool update_state(LowLevelStepAlgorithmType &step_alg, std::shared_ptr< KUKA::FRI::ClientData > &friData, boost::system::error_code &receive_ec, std::size_t &receive_bytes_transferred, boost::system::error_code &send_ec, std::size_t &send_bytes_transferred)
Updates the passed friData shared pointer to a pointer to newly updated data, plus any errors that oc...
time step between measurements
Definition: tags.hpp:24
void update_state(boost::asio::ip::udp::socket &socket, LowLevelStepAlgorithmType &step_alg, KUKA::FRI::ClientData &friData, boost::system::error_code &receive_ec, std::size_t &receive_bytes_transferred, boost::system::error_code &send_ec, std::size_t &send_bytes_transferred, boost::asio::ip::udp::endpoint sender_endpoint=boost::asio::ip::udp::endpoint())
Actually talk over the network to receive an update and send out a new KUKA FRI command.
boost::container::static_vector< double, KUKA::LBRState::NUM_DOF > joint_state
Definition: Kuka.hpp:42
KukaFRIdriver(Params params=defaultParams())
external forces, i.e. forces applied to an arm excluding those caused by the mass of the arm itself a...
Definition: tags.hpp:67
flatbuffer::ESessionState sessionState
Definition: Kuka.hpp:64
std::tuple< std::string, std::string, std::string, std::string, std::string, ThreadingRunMode > Params
Definition: Kuka.hpp:207
EClientCommandMode
Type of command being sent to the arm (Dimensonal units)
flatbuffer::EConnectionQuality connectionQuality
Definition: Kuka.hpp:66
joint_state commandedTorque
Definition: Kuka.hpp:54
static const Params defaultParams()
Definition: Kuka.hpp:209
single point in time relative to some start point
Definition: tags.hpp:21
Internal class, defines some default status variables.
Definition: Kuka.hpp:189
KukaFRIClientDataDriver(boost::asio::io_service &ios, Params params=defaultParams())
void operator()(ArmData &friData, revolute_joint_angle_open_chain_command_tag)