UlcNode.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2018, Dataspeed Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Dataspeed Inc. nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 #include "UlcNode.h"
37 
38 namespace dataspeed_ulc_can
39 {
40 
41 template <typename T>
42 static void getParamWithSaturation(ros::NodeHandle &nh, const std::string& key, T& value, T min, T max)
43 {
44  if (nh.getParam(key, value)) {
45  if (value < min) {
46  value = min;
47  nh.setParam(key, value);
48  } else if (value > max) {
49  value = max;
50  nh.setParam(key, value);
51  }
52  }
53 }
54 
55 template <class T>
56 static T overflowSaturation(double input, T limit_min, T limit_max, double scale_factor, const std::string& input_name, const std::string& units)
57 {
58  if (input < (limit_min * scale_factor)) {
59  ROS_WARN("%s [%f %s] out of range -- saturating to %f %s", input_name.c_str(), input, units.c_str(), limit_min * scale_factor, units.c_str());
60  return limit_min;
61  } else if (input > (limit_max * scale_factor)) {
62  ROS_WARN("%s [%f %s] out of range -- saturating to %f %s", input_name.c_str(), input, units.c_str(), limit_max * scale_factor, units.c_str());
63  return limit_max;
64  } else {
65  return input / scale_factor;
66  }
67 }
68 
69 static inline bool validInputs(const dataspeed_ulc_msgs::UlcCmd& cmd)
70 {
71  bool valid = true;
72  if (std::isnan(cmd.linear_velocity)) {
73  ROS_WARN("NaN input detected on speed input");
74  valid = false;
75  }
76  if (std::isnan(cmd.yaw_command)) {
77  ROS_WARN("NaN input detected on yaw command input");
78  valid = false;
79  }
80  if (std::isnan(cmd.linear_accel)) {
81  ROS_WARN("NaN input detected on linear accel input");
82  valid = false;
83  }
84  if (std::isnan(cmd.linear_decel)) {
85  ROS_WARN("NaN input detected on linear decel input");
86  valid = false;
87  }
88  if (std::isnan(cmd.lateral_accel)) {
89  ROS_WARN("NaN input detected on lateral accel input");
90  valid = false;
91  }
92  if (std::isnan(cmd.angular_accel)) {
93  ROS_WARN("NaN input detected on angular accel input");
94  valid = false;
95  }
96  return valid;
97 }
98 
100  enable_(false)
101 {
102  // Setup publishers
103  pub_report_ = n.advertise<dataspeed_ulc_msgs::UlcReport>("ulc_report", 2);
104  pub_can_ = n.advertise<can_msgs::Frame>("can_tx", 100);
105 
106  // Setup subscribers
108  sub_can_ = n.subscribe<can_msgs::Frame>("can_rx", 100, &UlcNode::recvCan, this, NODELAY);
109  sub_cmd_ = n.subscribe<dataspeed_ulc_msgs::UlcCmd>("ulc_cmd", 2, &UlcNode::recvUlcCmd, this, NODELAY);
110  sub_twist_ = n.subscribe<geometry_msgs::Twist>("cmd_vel", 2, &UlcNode::recvTwist, this, NODELAY);
111  sub_twist_stamped_ = n.subscribe<geometry_msgs::TwistStamped>("cmd_vel_stamped", 2, &UlcNode::recvTwistStamped, this, NODELAY);
112  sub_enable_ = n.subscribe<std_msgs::Bool>("dbw_enabled", 2, &UlcNode::recvEnable, this, NODELAY);
113 
114  // Setup timer for config message retransmission
115  double freq = 5.0;
116  getParamWithSaturation(pn, "config_frequency", freq, 5.0, 50.0);
118 }
119 
120 void UlcNode::recvEnable(const std_msgs::BoolConstPtr& msg)
121 {
122  enable_ = msg->data;
123 }
124 
125 void UlcNode::recvUlcCmd(const dataspeed_ulc_msgs::UlcCmdConstPtr& msg)
126 {
127  // Check for differences in acceleration limits
128  bool diff = (msg->linear_accel != ulc_cmd_.linear_accel)
129  || (msg->linear_decel != ulc_cmd_.linear_decel)
130  || (msg->lateral_accel != ulc_cmd_.lateral_accel)
131  || (msg->angular_accel != ulc_cmd_.angular_accel);
132  ulc_cmd_ = *msg;
133 
134  // Publish command message
135  sendCmdMsg(true);
136 
137  // Publish config message on change
138  if (diff) {
139  sendCfgMsg();
140  }
141 }
142 
143 void UlcNode::recvTwistCmd(const geometry_msgs::Twist& msg)
144 {
145  // Populate command fields
146  ulc_cmd_.linear_velocity = msg.linear.x;
147  ulc_cmd_.yaw_command = msg.angular.z;
148  ulc_cmd_.steering_mode = dataspeed_ulc_msgs::UlcCmd::YAW_RATE_MODE;
149 
150  // Set other fields to default values
151  ulc_cmd_.clear = false;
152  ulc_cmd_.enable_pedals = true;
153  ulc_cmd_.enable_shifting = true;
154  ulc_cmd_.enable_steering = true;
155  ulc_cmd_.shift_from_park = false;
156  ulc_cmd_.linear_accel = 0;
157  ulc_cmd_.linear_decel = 0;
158  ulc_cmd_.angular_accel = 0;
159  ulc_cmd_.lateral_accel = 0;
160 
161  // Publish command message
162  sendCmdMsg(false);
163 }
164 
165 void UlcNode::recvTwist(const geometry_msgs::TwistConstPtr& msg)
166 {
167  recvTwistCmd(*msg);
168 }
169 
170 void UlcNode::recvTwistStamped(const geometry_msgs::TwistStampedConstPtr& msg)
171 {
172  recvTwistCmd(msg->twist);
173 }
174 
175 void UlcNode::recvCan(const can_msgs::FrameConstPtr& msg)
176 {
177  if (!msg->is_rtr && !msg->is_error && !msg->is_extended) {
178  switch (msg->id) {
179  case ID_ULC_REPORT:
180  if (msg->dlc >= sizeof(MsgUlcReport)) {
181  const MsgUlcReport *ptr = (const MsgUlcReport *)msg->data.elems;
182  dataspeed_ulc_msgs::UlcReport report;
183  report.header.stamp = msg->header.stamp;
184  report.speed_ref = (float)ptr->speed_ref * 0.02f;
185  report.accel_ref = (float)ptr->accel_ref * 0.05f;
186  report.speed_meas = (float)ptr->speed_meas * 0.02f;
187  report.accel_meas = (float)ptr->accel_meas * 0.05f;
188  report.max_steering_angle = (float)ptr->max_steering_angle * 5.0f;
189  report.max_steering_vel = (float)ptr->max_steering_vel * 8.0f;
190  report.pedals_enabled = ptr->pedals_enabled;
191  report.steering_enabled = ptr->steering_enabled;
192  report.tracking_mode = ptr->tracking_mode;
193  report.speed_preempted = ptr->speed_preempted;
194  report.steering_preempted = ptr->steering_preempted;
195  report.override_latched = ptr->override;
196  report.steering_mode = ptr->steering_mode;
197  report.timeout = ptr->timeout;
198  pub_report_.publish(report);
199  }
200  break;
201  }
202  }
203 }
204 
205 void UlcNode::sendCmdMsg(bool cfg)
206 {
207  // Validate input fields
208  if (validInputs(ulc_cmd_)) {
209  if (cfg) {
211  }
212  } else {
213  cmd_stamp_ = ros::Time(0);
214  return;
215  }
216 
217  // Build CAN message
218  can_msgs::Frame msg;
219  msg.id = ID_ULC_CMD;
220  msg.is_extended = false;
221  msg.dlc = sizeof(MsgUlcCmd);
222  MsgUlcCmd *ptr = (MsgUlcCmd *)msg.data.elems;
223  memset(ptr, 0x00, sizeof(*ptr));
224 
225  // Populate enable bits
226  if (enable_) {
227  ptr->enable_pedals = ulc_cmd_.enable_pedals;
228  ptr->enable_steering = ulc_cmd_.enable_steering;
229  ptr->enable_shifting = ulc_cmd_.enable_shifting;
230  ptr->shift_from_park = ulc_cmd_.shift_from_park;
231  }
232 
233  // Populate command fields
234  ptr->clear = ulc_cmd_.clear;
235  ptr->linear_velocity = overflowSaturation(ulc_cmd_.linear_velocity, INT16_MIN, INT16_MAX, 0.0025, "ULC command speed", "m/s");
236  ptr->steering_mode = ulc_cmd_.steering_mode;
237  if (ulc_cmd_.steering_mode == dataspeed_ulc_msgs::UlcCmd::YAW_RATE_MODE) {
238  ptr->yaw_command = overflowSaturation(ulc_cmd_.yaw_command, INT16_MIN, INT16_MAX, 0.00025, "ULC yaw rate command", "rad/s");
239  } else if (ulc_cmd_.steering_mode == dataspeed_ulc_msgs::UlcCmd::CURVATURE_MODE) {
240  ptr->yaw_command = overflowSaturation(ulc_cmd_.yaw_command, INT16_MIN, INT16_MAX, 0.0000061, "ULC curvature command", "1/m");
241  } else {
242  ptr->yaw_command = 0;
243  ROS_WARN_THROTTLE(1.0, "Unsupported ULC steering control mode [%d]", ulc_cmd_.steering_mode);
244  cmd_stamp_ = ros::Time(0);
245  return;
246  }
247 
248  // Publish message
249  pub_can_.publish(msg);
250 }
251 
253 {
254  // Build CAN message
255  can_msgs::Frame msg;
256  msg.id = ID_ULC_CONFIG;
257  msg.is_extended = false;
258  msg.dlc = sizeof(MsgUlcCfg);
259  MsgUlcCfg *ptr = (MsgUlcCfg *)msg.data.elems;
260  memset(ptr, 0x00, sizeof(*ptr));
261 
262  // Populate acceleration limits
263  ptr->linear_accel = overflowSaturation(ulc_cmd_.linear_accel, 0, UINT8_MAX, 0.025, "Linear accel limit", "m/s^2");
264  ptr->linear_decel = overflowSaturation(ulc_cmd_.linear_decel, 0, UINT8_MAX, 0.025, "Linear decel limit", "m/s^2");
265  ptr->lateral_accel = overflowSaturation(ulc_cmd_.lateral_accel, 0, UINT8_MAX, 0.05, "Lateral accel limit", "m/s^2");
266  ptr->angular_accel = overflowSaturation(ulc_cmd_.angular_accel, 0, UINT8_MAX, 0.02, "Angular accel limit", "rad/s^2");
267 
268  // Publish message
269  pub_can_.publish(msg);
270 
271  // Reset timer
274 }
275 
277 {
278  // Retransmit config message while command is valid
279  if (event.current_real - cmd_stamp_ < ros::Duration(0.1)) {
280  sendCfgMsg();
281  }
282 }
283 
284 }
ros::Subscriber sub_twist_stamped_
Definition: UlcNode.h:66
void recvEnable(const std_msgs::BoolConstPtr &msg)
Definition: UlcNode.cpp:120
#define ROS_WARN_THROTTLE(rate,...)
void publish(const boost::shared_ptr< M > &message) const
void recvTwistCmd(const geometry_msgs::Twist &msg)
Definition: UlcNode.cpp:143
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void sendCmdMsg(bool cfg)
Definition: UlcNode.cpp:205
void recvCan(const can_msgs::FrameConstPtr &msg)
Definition: UlcNode.cpp:175
ros::NodeHandle * n
void stop()
ros::NodeHandle * pn
void start()
#define ROS_WARN(...)
ros::Subscriber sub_cmd_
Definition: UlcNode.h:64
dataspeed_ulc_msgs::UlcCmd ulc_cmd_
Definition: UlcNode.h:73
TransportHints & tcpNoDelay(bool nodelay=true)
void recvTwist(const geometry_msgs::TwistConstPtr &msg)
Definition: UlcNode.cpp:165
UlcNode(ros::NodeHandle &n, ros::NodeHandle &pn)
Definition: UlcNode.cpp:99
ros::Timer config_timer_
Definition: UlcNode.h:71
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
void recvTwistStamped(const geometry_msgs::TwistStampedConstPtr &msg)
Definition: UlcNode.cpp:170
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
ros::Subscriber sub_can_
Definition: UlcNode.h:67
ros::Subscriber sub_enable_
Definition: UlcNode.h:68
ros::Publisher pub_can_
Definition: UlcNode.h:70
bool getParam(const std::string &key, std::string &s) const
ros::Publisher pub_report_
Definition: UlcNode.h:69
static T overflowSaturation(double input, T limit_min, T limit_max, double scale_factor, const std::string &input_name, const std::string &units)
Definition: UlcNode.cpp:56
static Time now()
ros::Subscriber sub_twist_
Definition: UlcNode.h:65
static void getParamWithSaturation(ros::NodeHandle &nh, const std::string &key, T &value, T min, T max)
Definition: UlcNode.cpp:42
void recvUlcCmd(const dataspeed_ulc_msgs::UlcCmdConstPtr &msg)
Definition: UlcNode.cpp:125
static bool validInputs(const dataspeed_ulc_msgs::UlcCmd &cmd)
Definition: UlcNode.cpp:69
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const
void configTimerCb(const ros::TimerEvent &event)
Definition: UlcNode.cpp:276


dataspeed_ulc_can
Author(s): Micho Radovnikovich
autogenerated on Thu Jul 9 2020 03:45:43