control_plugin.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
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 
17 
18 #include <math.h>
19 #include <angles/angles.h>
20 #include <boost/scoped_ptr.hpp>
21 #include <boost/thread/mutex.hpp>
22 
26 
28 #include <cob_base_controller_utils/WheelCommands.h>
29 #include <geometry_msgs/Twist.h>
30 
32 
34 
36 {
37 
38 double limitValue(double value, double limit){
39  if(limit != 0){
40  if (value > limit){
41  value = limit;
42  } else if (value < -limit) {
43  value = -limit;
44  }
45  }
46  return value;
47 }
48 
49 
50 class WheelController : public controller_interface::MultiInterfaceController<hardware_interface::VelocityJointInterface, hardware_interface::PositionJointInterface>
51 {
52 public:
53  virtual bool init(hardware_interface::RobotHW* robot_hw, ros::NodeHandle &nh)
54  {
55  if (!nh.getParam("steer_joint", wheel_state_.steer_name)){
56  ROS_ERROR("Parameter 'steer_joint' not set");
57  return false;
58  }
59  if (!nh.getParam("drive_joint", wheel_state_.drive_name)){
60  ROS_ERROR("Parameter 'drive_joint' not set");
61  return false;
62  }
63 
66  steer_joint_ = p->getHandle(wheel_state_.steer_name);
68 
69  urdf::Model model;
70  std::string description_name;
71  bool has_model = nh.searchParam("robot_description", description_name) && model.initParam(description_name);
72 
73  urdf::Vector3 steer_pos;
74  urdf::JointConstSharedPtr steer_joint;
75  urdf::JointConstSharedPtr drive_joint;
76 
77  if(has_model){
78  steer_joint = model.getJoint(wheel_state_.steer_name);
79  if(steer_joint){
80  tf2::Transform transform;
81  //root link for tricycle is "base_pivot_link"
82  if(parseWheelTransform(wheel_state_.steer_name, model.getRoot()->name, transform, &model)){
83  wheel_state_.pos_x = transform.getOrigin().getX();
84  wheel_state_.pos_y = transform.getOrigin().getY();
85  wheel_state_.radius = transform.getOrigin().getZ();
86  wheel_state_.sign = cos(transform.getRotation().getAngle());
87  }
88  nh.param("max_steer_rate", max_steer_rate_, 0.0);
89  max_steer_rate_ = (max_steer_rate_ != 0.0 && max_steer_rate_ < steer_joint->limits->velocity) ? max_steer_rate_ : steer_joint->limits->velocity;
90  }
91  drive_joint = model.getJoint(wheel_state_.drive_name);
92  if(drive_joint){
93  nh.param("max_drive_rate", max_drive_rate_, 0.0);
94  max_drive_rate_ = (max_drive_rate_ != 0.0 && max_drive_rate_ < drive_joint->limits->velocity) ? max_drive_rate_ : drive_joint->limits->velocity;
95  }
96  }
97 
98  nh.param("max_trans_velocity", max_vel_trans_, 0.0);
99  if(max_vel_trans_ < 0){
100  ROS_ERROR_STREAM("max_trans_velocity must be non-negative.");
101  return false;
102  }
103  nh.param("max_rot_velocity", max_vel_rot_, 0.0);
104  if(max_vel_rot_ < 0){
105  ROS_ERROR_STREAM("max_rot_velocity must be non-negative.");
106  return false;
107  }
108  double timeout;
109  nh.param("timeout", timeout, 1.0);
110  if(timeout < 0){
111  ROS_ERROR_STREAM("timeout must be non-negative.");
112  return false;
113  }
114  timeout_.fromSec(timeout);
115 
116  pub_divider_ = nh.param("pub_divider", 0);
119 
120  commands_pub_->msg_.drive_target_velocity.resize(1);
121  commands_pub_->msg_.steer_target_velocity.resize(1);
122  commands_pub_->msg_.steer_target_position.resize(1);
123  commands_pub_->msg_.steer_target_error.resize(1);
124 
125  return true;
126  }
127 
128  virtual void starting(const ros::Time& time){
129  cycles_ = 0;
130  }
131 
132  virtual void update(const ros::Time& time, const ros::Duration& period)
133  {
134  {
135  boost::mutex::scoped_try_lock lock(mutex_);
136  if(lock){
137  Target target = target_;
138  target_.updated = false;
139 
140  if(!target.stamp.isZero() && !timeout_.isZero() && (time - target.stamp) > timeout_){
141  ROS_WARN_STREAM("target timed out");
142  target_.stamp = ros::Time(); // only reset once
143  target.state = PlatformState();
144  target.updated = true;
145  }
146  lock.unlock();
147  }
148  }
149 
150  updateCommand();
151 
154 
155  if(cycles_ < pub_divider_ && (++cycles_) == pub_divider_){
156  if(commands_pub_->trylock()){
157  ++(commands_pub_->msg_.header.seq);
158  commands_pub_->msg_.header.stamp = time;
159 
160  commands_pub_->msg_.drive_target_velocity[0] = wheel_command_.drive_vel;
161  commands_pub_->msg_.steer_target_velocity[0] = 0.0;
162  commands_pub_->msg_.steer_target_position[0] = wheel_command_.steer_pos;
163  commands_pub_->msg_.steer_target_error[0] = wheel_command_.steer_pos - wheel_state_.steer_pos;
164  commands_pub_->unlockAndPublish();
165  }
166  cycles_ = 0;
167  }
168  }
169 
170 private:
171  struct Target {
173  bool updated;
175  } target_;
176 
181 
182  boost::mutex mutex_;
184 
185  boost::scoped_ptr<realtime_tools::RealtimePublisher<cob_base_controller_utils::WheelCommands> > commands_pub_;
186  uint32_t cycles_;
187  uint32_t pub_divider_;
188 
192 
193  void topicCallbackTwistCmd(const geometry_msgs::Twist::ConstPtr& msg){
194  if(this->isRunning()){
195  boost::mutex::scoped_lock lock(mutex_);
196  if(isnan(msg->linear.x) || isnan(msg->linear.y) || isnan(msg->angular.z)) {
197  ROS_FATAL("Received NaN-value in Twist message. Reset target to zero.");
199  }else{
200  target_.state.velX = limitValue(msg->linear.x, max_vel_trans_);
201  target_.state.velY = limitValue(msg->linear.y, max_vel_trans_);
202  target_.state.rotTheta = limitValue(msg->angular.z, max_vel_rot_);
203  }
204  target_.updated = true;
206  }
207  }
208 
210  //get JointState from JointHandles
211  wheel_state_.steer_pos = steer_joint_.getPosition();
212  wheel_state_.steer_vel = steer_joint_.getVelocity();
213  wheel_state_.drive_pos = drive_joint_.getPosition();
214  wheel_state_.drive_vel = drive_joint_.getVelocity();
215 
216  // calculate inverse kinematics
217  // http://www.wolframalpha.com/input/?i=Solve%5Bx%3D%3Dw*cos(a),+phi%3D%3Dw*sin(a)%2Fr,a,w%5D
218  double r_base = wheel_state_.pos_x * wheel_state_.sign;
219  double k = sqrt(pow(r_base,2)*pow(target_.state.rotTheta,2) + pow(target_.state.velX,2));
220  if (target_.state.rotTheta != 0)
221  {
222  double a1 = 2 * atan2(k - target_.state.velX, r_base*target_.state.rotTheta);
223  a1 = angles::normalize_angle(a1);
224  double b1 = k / wheel_state_.radius;
225 
226  double a2 = -2 * atan2(k + target_.state.velX, r_base*target_.state.rotTheta);
227  a2 = angles::normalize_angle(a2);
228  double b2 = - k / wheel_state_.radius;
229 
230  if (fabs(a1-wheel_state_.steer_pos) <= fabs(a2-wheel_state_.steer_pos))
231  {
232  wheel_command_.steer_pos = a1;
233  wheel_command_.drive_vel = b1;
234  }
235  else
236  {
237  wheel_command_.steer_pos = a2;
238  wheel_command_.drive_vel = b2;
239  }
240  }
241  else if (target_.state.velX != 0)
242  {
243  if (fabs(0.0-wheel_state_.steer_pos) <= fabs(M_PI-wheel_state_.steer_pos))
244  {
245  wheel_command_.steer_pos = 0.0;
246  wheel_command_.drive_vel = target_.state.velX / wheel_state_.radius;
247  }
248  else
249  {
250  wheel_command_.steer_pos = M_PI;
251  wheel_command_.drive_vel = -target_.state.velX / wheel_state_.radius;
252  }
253  }
254  else {
255  wheel_command_.steer_pos = wheel_state_.steer_pos;
256  wheel_command_.drive_vel = 0.0;
257  }
258 
259  wheel_command_.drive_vel = limitValue(wheel_command_.drive_vel, max_drive_rate_);
260  }
261 };
262 
263 }
264 
#define ROS_FATAL(...)
void topicCallbackTwistCmd(const geometry_msgs::Twist::ConstPtr &msg)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
Quaternion getRotation() const
std::string drive_name
virtual void starting(const ros::Time &time)
std::string steer_name
tf2Scalar getAngle() const
boost::scoped_ptr< realtime_tools::RealtimePublisher< cob_base_controller_utils::WheelCommands > > commands_pub_
struct cob_tricycle_controller::WheelController::Target target_
Duration & fromSec(double t)
TF2SIMD_FORCE_INLINE Vector3 & getOrigin()
hardware_interface::JointHandle drive_joint_
bool searchParam(const std::string &key, std::string &result) const
bool param(const std::string &param_name, T &param_val, const T &default_val) const
double limitValue(double value, double limit)
bool parseWheelTransform(const std::string &joint_name, const std::string &parent_link_name, tf2::Transform &transform, urdf::Model *model)
Definition: param_parser.h:27
def normalize_angle(angle)
URDF_EXPORT bool initParam(const std::string &param)
#define ROS_WARN_STREAM(args)
JointHandle getHandle(const std::string &name)
void setCommand(double command)
virtual void update(const ros::Time &time, const ros::Duration &period)
bool getParam(const std::string &key, std::string &s) const
static Time now()
hardware_interface::JointHandle steer_joint_
virtual bool init(hardware_interface::RobotHW *robot_hw, ros::NodeHandle &nh)
#define ROS_ERROR_STREAM(args)
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
#define ROS_ERROR(...)


cob_tricycle_controller
Author(s): Felix Messmer
autogenerated on Thu Apr 8 2021 02:39:57