vesc_to_odom.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 <cmath>
31 #include <string>
32 
33 #include <nav_msgs/Odometry.h>
34 #include <geometry_msgs/TransformStamped.h>
35 
36 namespace vesc_ackermann
37 {
38 
39 template <typename T>
40 inline bool getRequiredParam(const ros::NodeHandle& nh, const std::string& name, T* value);
41 
43  odom_frame_("odom"), base_frame_("base_link"),
44  use_servo_cmd_(true), publish_tf_(false), x_(0.0), y_(0.0), yaw_(0.0)
45 {
46  // get ROS parameters
47  private_nh.param("odom_frame", odom_frame_, odom_frame_);
48  private_nh.param("base_frame", base_frame_, base_frame_);
49  private_nh.param("use_servo_cmd_to_calc_angular_velocity", use_servo_cmd_, use_servo_cmd_);
50  if (!getRequiredParam(nh, "speed_to_erpm_gain", &speed_to_erpm_gain_))
51  return;
52  if (!getRequiredParam(nh, "speed_to_erpm_offset", &speed_to_erpm_offset_))
53  return;
54  if (use_servo_cmd_)
55  {
56  if (!getRequiredParam(nh, "steering_angle_to_servo_gain", &steering_to_servo_gain_))
57  return;
58  if (!getRequiredParam(nh, "steering_angle_to_servo_offset", &steering_to_servo_offset_))
59  return;
60  if (!getRequiredParam(nh, "wheelbase", &wheelbase_))
61  return;
62  }
63  private_nh.param("publish_tf", publish_tf_, publish_tf_);
64 
65  // create odom publisher
66  odom_pub_ = nh.advertise<nav_msgs::Odometry>("odom", 10);
67 
68  // create tf broadcaster
69  if (publish_tf_)
70  {
72  }
73 
74  // subscribe to vesc state and. optionally, servo command
75  vesc_state_sub_ = nh.subscribe("sensors/core", 10, &VescToOdom::vescStateCallback, this);
76  if (use_servo_cmd_)
77  {
78  servo_sub_ = nh.subscribe("sensors/servo_position_command", 10,
80  }
81 }
82 
83 void VescToOdom::vescStateCallback(const vesc_msgs::VescStateStamped::ConstPtr& state)
84 {
85  // check that we have a last servo command if we are depending on it for angular velocity
87  return;
88 
89  // convert to engineering units
90  double current_speed = (-state->state.speed - speed_to_erpm_offset_) / speed_to_erpm_gain_;
91  if (std::fabs(current_speed) < 0.05)
92  {
93  current_speed = 0.0;
94  }
95  double current_steering_angle(0.0), current_angular_velocity(0.0);
96  if (use_servo_cmd_)
97  {
98  current_steering_angle =
100  current_angular_velocity = current_speed * tan(current_steering_angle) / wheelbase_;
101  }
102 
103  // use current state as last state if this is our first time here
104  if (!last_state_)
105  last_state_ = state;
106 
107  // calc elapsed time
108  ros::Duration dt = state->header.stamp - last_state_->header.stamp;
109 
112  // propigate odometry
113  double x_dot = current_speed * cos(yaw_);
114  double y_dot = current_speed * sin(yaw_);
115  x_ += x_dot * dt.toSec();
116  y_ += y_dot * dt.toSec();
117  if (use_servo_cmd_)
118  yaw_ += current_angular_velocity * dt.toSec();
119 
120  // save state for next time
121  last_state_ = state;
122 
123  // publish odometry message
124  nav_msgs::Odometry::Ptr odom(new nav_msgs::Odometry);
125  odom->header.frame_id = odom_frame_;
126  odom->header.stamp = state->header.stamp;
127  odom->child_frame_id = base_frame_;
128 
129  // Position
130  odom->pose.pose.position.x = x_;
131  odom->pose.pose.position.y = y_;
132  odom->pose.pose.orientation.x = 0.0;
133  odom->pose.pose.orientation.y = 0.0;
134  odom->pose.pose.orientation.z = sin(yaw_ / 2.0);
135  odom->pose.pose.orientation.w = cos(yaw_ / 2.0);
136 
137  // Position uncertainty
139  odom->pose.covariance[0] = 0.2;
140  odom->pose.covariance[7] = 0.2;
141  odom->pose.covariance[35] = 0.4;
142 
143  // Velocity ("in the coordinate frame given by the child_frame_id")
144  odom->twist.twist.linear.x = current_speed;
145  odom->twist.twist.linear.y = 0.0;
146  odom->twist.twist.angular.z = current_angular_velocity;
147 
148  // Velocity uncertainty
151  if (publish_tf_)
152  {
153  geometry_msgs::TransformStamped tf;
154  tf.header.frame_id = odom_frame_;
155  tf.child_frame_id = base_frame_;
156  tf.header.stamp = ros::Time::now();
157  tf.transform.translation.x = x_;
158  tf.transform.translation.y = y_;
159  tf.transform.translation.z = 0.0;
160  tf.transform.rotation = odom->pose.pose.orientation;
161  if (ros::ok())
162  {
163  tf_pub_->sendTransform(tf);
164  }
165  }
166 
167  if (ros::ok())
168  {
169  odom_pub_.publish(odom);
170  }
171 }
172 
173 void VescToOdom::servoCmdCallback(const std_msgs::Float64::ConstPtr& servo)
174 {
175  last_servo_cmd_ = servo;
176 }
177 
178 template <typename T>
179 inline bool getRequiredParam(const ros::NodeHandle& nh, const std::string& name, T* value)
180 {
181  if (nh.getParam(name, *value))
182  return true;
183 
184  ROS_FATAL("VescToOdom: Parameter %s is required.", name.c_str());
185  return false;
186 }
187 
188 } // namespace vesc_ackermann
void vescStateCallback(const vesc_msgs::VescStateStamped::ConstPtr &state)
#define ROS_FATAL(...)
void publish(const boost::shared_ptr< M > &message) const
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
vesc_msgs::VescStateStamped::ConstPtr last_state_
Last received state message.
Definition: vesc_to_odom.h:62
std::shared_ptr< tf::TransformBroadcaster > tf_pub_
Definition: vesc_to_odom.h:68
ros::Publisher odom_pub_
Definition: vesc_to_odom.h:65
void servoCmdCallback(const std_msgs::Float64::ConstPtr &servo)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
ROSCPP_DECL bool ok()
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
std_msgs::Float64::ConstPtr last_servo_cmd_
Last servo position commanded value.
Definition: vesc_to_odom.h:61
ros::Subscriber vesc_state_sub_
Definition: vesc_to_odom.h:66
ros::Subscriber servo_sub_
Definition: vesc_to_odom.h:67
bool getParam(const std::string &key, std::string &s) const
static Time now()
bool getRequiredParam(const ros::NodeHandle &nh, const std::string &name, T *value)
VescToOdom(ros::NodeHandle nh, ros::NodeHandle private_nh)


vesc_ackermann
Author(s): Michael T. Boulet , Joshua Whitley
autogenerated on Sun Apr 18 2021 02:47:57