vesc_driver.cpp
Go to the documentation of this file.
1 // Copyright 2020 F1TENTH Foundation
2 //
3 // Redistribution and use in source and binary forms, with or without modification, are permitted
4 // provided that the following conditions are met:
5 //
6 // 1. Redistributions of source code must retain the above copyright notice, this list of conditions
7 // and the following disclaimer.
8 //
9 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
10 // of conditions and the following disclaimer in the documentation and/or other materials
11 // provided with the distribution.
12 //
13 // 3. Neither the name of the copyright holder nor the names of its contributors may be used
14 // to endorse or promote products derived from this software without specific prior
15 // written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
18 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19 // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
24 // WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 // -*- mode:c++; fill-column: 100; -*-
27 
29 
30 #include <cassert>
31 #include <cmath>
32 #include <memory>
33 #include <sstream>
34 #include <string>
35 
36 #include <vesc_msgs/VescStateStamped.h>
37 
38 namespace vesc_driver
39 {
40 
41 using std::placeholders::_1;
42 
44  ros::NodeHandle private_nh) :
45  vesc_(std::string(),
46  std::bind(&VescDriver::vescPacketCallback, this, _1),
47  std::bind(&VescDriver::vescErrorCallback, this, _1)),
48  duty_cycle_limit_(private_nh, "duty_cycle", -1.0, 1.0), current_limit_(private_nh, "current"),
49  brake_limit_(private_nh, "brake"), speed_limit_(private_nh, "speed"),
50  position_limit_(private_nh, "position"), servo_limit_(private_nh, "servo", 0.0, 1.0),
51  driver_mode_(MODE_INITIALIZING), fw_version_major_(-1), fw_version_minor_(-1)
52 {
53  // get vesc serial port address
54  std::string port;
55  if (!private_nh.getParam("port", port))
56  {
57  ROS_FATAL("VESC communication port parameter required.");
58  ros::shutdown();
59  return;
60  }
61 
62  // attempt to connect to the serial port
63  try
64  {
65  vesc_.connect(port);
66  }
67  catch (SerialException e)
68  {
69  ROS_FATAL("Failed to connect to the VESC, %s.", e.what());
70  ros::shutdown();
71  return;
72  }
73 
74  // create vesc state (telemetry) publisher
75  state_pub_ = nh.advertise<vesc_msgs::VescStateStamped>("sensors/core", 10);
76 
77  // since vesc state does not include the servo position, publish the commanded
78  // servo position as a "sensor"
79  servo_sensor_pub_ = nh.advertise<std_msgs::Float64>("sensors/servo_position_command", 10);
80 
81  // subscribe to motor and servo command topics
82  duty_cycle_sub_ = nh.subscribe("commands/motor/duty_cycle", 10,
84  current_sub_ = nh.subscribe("commands/motor/current", 10, &VescDriver::currentCallback, this);
85  brake_sub_ = nh.subscribe("commands/motor/brake", 10, &VescDriver::brakeCallback, this);
86  speed_sub_ = nh.subscribe("commands/motor/speed", 10, &VescDriver::speedCallback, this);
87  position_sub_ = nh.subscribe("commands/motor/position", 10, &VescDriver::positionCallback, this);
88  servo_sub_ = nh.subscribe("commands/servo/position", 10, &VescDriver::servoCallback, this);
89 
90  // create a 50Hz timer, used for state machine & polling VESC telemetry
92 }
93 
94 /* TODO or TO-THINKABOUT LIST
95  - what should we do on startup? send brake or zero command?
96  - what to do if the vesc interface gives an error?
97  - check version number against know compatable?
98  - should we wait until we receive telemetry before sending commands?
99  - should we track the last motor command
100  - what to do if no motor command received recently?
101  - what to do if no servo command received recently?
102  - what is the motor safe off state (0 current?)
103  - what to do if a command parameter is out of range, ignore?
104  - try to predict vesc bounds (from vesc config) and command detect bounds errors
105 */
106 
108 {
109  // VESC interface should not unexpectedly disconnect, but test for it anyway
110  if (!vesc_.isConnected())
111  {
112  ROS_FATAL("Unexpectedly disconnected from serial port.");
113  timer_.stop();
114  ros::shutdown();
115  return;
116  }
117 
118  /*
119  * Driver state machine, modes:
120  * INITIALIZING - request and wait for vesc version
121  * OPERATING - receiving commands from subscriber topics
122  */
124  {
125  // request version number, return packet will update the internal version numbers
127  if (fw_version_major_ >= 0 && fw_version_minor_ >= 0)
128  {
129  ROS_INFO("Connected to VESC with firmware version %d.%d",
132  }
133  }
134  else if (driver_mode_ == MODE_OPERATING)
135  {
136  // poll for vesc state (telemetry)
138  }
139  else
140  {
141  // unknown mode, how did that happen?
142  assert(false && "unknown driver mode");
143  }
144 }
145 
146 void VescDriver::vescPacketCallback(const std::shared_ptr<VescPacket const>& packet)
147 {
148  if (packet->name() == "Values")
149  {
150  std::shared_ptr<VescPacketValues const> values =
151  std::dynamic_pointer_cast<VescPacketValues const>(packet);
152 
153  vesc_msgs::VescStateStamped::Ptr state_msg(new vesc_msgs::VescStateStamped);
154  state_msg->header.stamp = ros::Time::now();
155  state_msg->state.voltage_input = values->v_in();
156  state_msg->state.temperature_pcb = values->temp_pcb();
157  state_msg->state.current_motor = values->current_motor();
158  state_msg->state.current_input = values->current_in();
159  state_msg->state.speed = values->rpm();
160  state_msg->state.duty_cycle = values->duty_now();
161  state_msg->state.charge_drawn = values->amp_hours();
162  state_msg->state.charge_regen = values->amp_hours_charged();
163  state_msg->state.energy_drawn = values->watt_hours();
164  state_msg->state.energy_regen = values->watt_hours_charged();
165  state_msg->state.displacement = values->tachometer();
166  state_msg->state.distance_traveled = values->tachometer_abs();
167  state_msg->state.fault_code = values->fault_code();
168 
169  state_pub_.publish(state_msg);
170  }
171  else if (packet->name() == "FWVersion")
172  {
173  std::shared_ptr<VescPacketFWVersion const> fw_version =
174  std::dynamic_pointer_cast<VescPacketFWVersion const>(packet);
175  // todo: might need lock here
176  fw_version_major_ = fw_version->fwMajor();
177  fw_version_minor_ = fw_version->fwMinor();
178  }
179 }
180 
181 void VescDriver::vescErrorCallback(const std::string& error)
182 {
183  ROS_ERROR("%s", error.c_str());
184 }
185 
191 void VescDriver::dutyCycleCallback(const std_msgs::Float64::ConstPtr& duty_cycle)
192 {
194  {
195  vesc_.setDutyCycle(duty_cycle_limit_.clip(duty_cycle->data));
196  }
197 }
198 
204 void VescDriver::currentCallback(const std_msgs::Float64::ConstPtr& current)
205 {
207  {
208  vesc_.setCurrent(current_limit_.clip(current->data));
209  }
210 }
211 
217 void VescDriver::brakeCallback(const std_msgs::Float64::ConstPtr& brake)
218 {
220  {
221  vesc_.setBrake(brake_limit_.clip(brake->data));
222  }
223 }
224 
231 void VescDriver::speedCallback(const std_msgs::Float64::ConstPtr& speed)
232 {
234  {
235  vesc_.setSpeed(speed_limit_.clip(speed->data));
236  }
237 }
238 
243 void VescDriver::positionCallback(const std_msgs::Float64::ConstPtr& position)
244 {
246  {
247  // ROS uses radians but VESC seems to use degrees. Convert to degrees.
248  double position_deg = position_limit_.clip(position->data) * 180.0 / M_PI;
249  vesc_.setPosition(position_deg);
250  }
251 }
252 
256 void VescDriver::servoCallback(const std_msgs::Float64::ConstPtr& servo)
257 {
259  {
260  double servo_clipped(servo_limit_.clip(servo->data));
261  vesc_.setServo(servo_clipped);
262  // publish clipped servo value as a "sensor"
263  std_msgs::Float64::Ptr servo_sensor_msg(new std_msgs::Float64);
264  servo_sensor_msg->data = servo_clipped;
265  servo_sensor_pub_.publish(servo_sensor_msg);
266  }
267 }
268 
269 VescDriver::CommandLimit::CommandLimit(const ros::NodeHandle& nh, const std::string& str,
270  const boost::optional<double>& min_lower,
271  const boost::optional<double>& max_upper) :
272  name(str)
273 {
274  // check if user's minimum value is outside of the range min_lower to max_upper
275  double param_min;
276  if (nh.getParam(name + "_min", param_min))
277  {
278  if (min_lower && param_min < *min_lower)
279  {
280  lower = *min_lower;
281  ROS_WARN_STREAM("Parameter " << name << "_min (" << param_min <<
282  ") is less than the feasible minimum (" << *min_lower << ").");
283  }
284  else if (max_upper && param_min > *max_upper)
285  {
286  lower = *max_upper;
287  ROS_WARN_STREAM("Parameter " << name << "_min (" << param_min <<
288  ") is greater than the feasible maximum (" << *max_upper << ").");
289  }
290  else
291  {
292  lower = param_min;
293  }
294  }
295  else if (min_lower)
296  {
297  lower = *min_lower;
298  }
299 
300  // check if the uers' maximum value is outside of the range min_lower to max_upper
301  double param_max;
302  if (nh.getParam(name + "_max", param_max))
303  {
304  if (min_lower && param_max < *min_lower)
305  {
306  upper = *min_lower;
307  ROS_WARN_STREAM("Parameter " << name << "_max (" << param_max <<
308  ") is less than the feasible minimum (" << *min_lower << ").");
309  }
310  else if (max_upper && param_max > *max_upper)
311  {
312  upper = *max_upper;
313  ROS_WARN_STREAM("Parameter " << name << "_max (" << param_max <<
314  ") is greater than the feasible maximum (" << *max_upper << ").");
315  }
316  else
317  {
318  upper = param_max;
319  }
320  }
321  else if (max_upper)
322  {
323  upper = *max_upper;
324  }
325 
326  // check for min > max
327  if (upper && lower && *lower > *upper)
328  {
329  ROS_WARN_STREAM("Parameter " << name << "_max (" << *upper
330  << ") is less than parameter " << name << "_min (" << *lower << ").");
331  double temp(*lower);
332  lower = *upper;
333  upper = temp;
334  }
335 
336  std::ostringstream oss;
337  oss << " " << name << " limit: ";
338  if (lower) oss << *lower << " ";
339  else oss << "(none) ";
340  if (upper) oss << *upper;
341  else oss << "(none)";
342  ROS_DEBUG_STREAM(oss.str());
343 }
344 
345 double VescDriver::CommandLimit::clip(double value)
346 {
347  if (lower && value < lower)
348  {
349  ROS_INFO_THROTTLE(10, "%s command value (%f) below minimum limit (%f), clipping.",
350  name.c_str(), value, *lower);
351  return *lower;
352  }
353  if (upper && value > upper)
354  {
355  ROS_INFO_THROTTLE(10, "%s command value (%f) above maximum limit (%f), clipping.",
356  name.c_str(), value, *upper);
357  return *upper;
358  }
359  return value;
360 }
361 
362 
363 } // namespace vesc_driver
CommandLimit current_limit_
Definition: vesc_driver.h:68
#define ROS_FATAL(...)
void dutyCycleCallback(const std_msgs::Float64::ConstPtr &duty_cycle)
int fw_version_major_
firmware major version reported by vesc
Definition: vesc_driver.h:95
ros::Subscriber servo_sub_
Definition: vesc_driver.h:82
void publish(const boost::shared_ptr< M > &message) const
CommandLimit speed_limit_
Definition: vesc_driver.h:70
ros::Subscriber brake_sub_
Definition: vesc_driver.h:79
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void vescPacketCallback(const std::shared_ptr< VescPacket const > &packet)
boost::optional< double > lower
Definition: vesc_driver.h:64
void servoCallback(const std_msgs::Float64::ConstPtr &servo)
void stop()
void setPosition(double position)
CommandLimit position_limit_
Definition: vesc_driver.h:71
driver_mode_t driver_mode_
driver state machine mode (state)
Definition: vesc_driver.h:94
void positionCallback(const std_msgs::Float64::ConstPtr &position)
ros::Subscriber position_sub_
Definition: vesc_driver.h:81
void setDutyCycle(double duty_cycle)
void connect(const std::string &port)
void currentCallback(const std_msgs::Float64::ConstPtr &current)
#define ROS_INFO(...)
CommandLimit duty_cycle_limit_
Definition: vesc_driver.h:67
void setCurrent(double current)
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
virtual const char * what() const
int fw_version_minor_
firmware minor version reported by vesc
Definition: vesc_driver.h:96
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void brakeCallback(const std_msgs::Float64::ConstPtr &brake)
#define ROS_WARN_STREAM(args)
#define ROS_DEBUG_STREAM(args)
void setBrake(double brake)
CommandLimit brake_limit_
Definition: vesc_driver.h:69
ros::Publisher state_pub_
Definition: vesc_driver.h:75
void setServo(double servo)
ros::Subscriber current_sub_
Definition: vesc_driver.h:78
ros::Publisher servo_sensor_pub_
Definition: vesc_driver.h:76
void speedCallback(const std_msgs::Float64::ConstPtr &speed)
void setSpeed(double speed)
VescDriver(ros::NodeHandle nh, ros::NodeHandle private_nh)
Definition: vesc_driver.cpp:43
bool getParam(const std::string &key, std::string &s) const
ros::Subscriber duty_cycle_sub_
Definition: vesc_driver.h:77
static Time now()
#define ROS_INFO_THROTTLE(rate,...)
ROSCPP_DECL void shutdown()
boost::optional< double > upper
Definition: vesc_driver.h:65
CommandLimit(const ros::NodeHandle &nh, const std::string &str, const boost::optional< double > &min_lower=boost::optional< double >(), const boost::optional< double > &max_upper=boost::optional< double >())
CommandLimit servo_limit_
Definition: vesc_driver.h:72
VescInterface vesc_
Definition: vesc_driver.h:52
void timerCallback(const ros::TimerEvent &event)
#define ROS_ERROR(...)
void vescErrorCallback(const std::string &error)
ros::Subscriber speed_sub_
Definition: vesc_driver.h:80


vesc_driver
Author(s): Michael T. Boulet , Joshua Whitley
autogenerated on Sun Apr 18 2021 02:48:01