18 #include <mavros_msgs/WheelOdomStamped.h> 20 #include <geometry_msgs/TwistWithCovarianceStamped.h> 21 #include <nav_msgs/Odometry.h> 27 namespace extra_plugins {
37 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
40 wo_nh(
"~wheel_odometry"),
119 if (separation < 1.
e-5) {
121 ROS_WARN_NAMED(
"wo",
"WO: Separation between the first two wheels is too small (%f).", separation);
153 ROS_WARN_NAMED(
"wo",
"WO: No odometry computations will be performed.");
221 Eigen::Rotation2Dd rot(yaw);
222 rpose.head(2) = rot * rpose.head(2);
225 ROS_INFO_NAMED(
"wo",
"WO: Initial yaw (deg): %f", yaw/M_PI*180.0);
226 yaw_initialized =
true;
230 geometry_msgs::Quaternion quat;
235 geometry_msgs::TwistWithCovariance twist_cov;
237 twist_cov.twist.linear.x =
rtwist(0);
238 twist_cov.twist.linear.y =
rtwist(1);
239 twist_cov.twist.linear.z = 0.0;
241 twist_cov.twist.angular.x = 0.0;
242 twist_cov.twist.angular.y = 0.0;
243 twist_cov.twist.angular.z =
rtwist(2);
246 twist_cov_map.setZero();
248 twist_cov_map.block<1, 1>(5, 5).diagonal() <<
rtwist_cov(2);
252 auto twist_cov_t = boost::make_shared<geometry_msgs::TwistWithCovarianceStamped>();
254 twist_cov_t->header.stamp = time;
255 twist_cov_t->header.frame_id =
frame_id;
257 twist_cov_t->twist = twist_cov;
259 twist_pub.
publish(twist_cov_t);
262 else if (yaw_initialized) {
263 auto odom = boost::make_shared<nav_msgs::Odometry>();
265 odom->header.stamp = time;
269 odom->pose.pose.position.x =
rpose(0);
270 odom->pose.pose.position.y =
rpose(1);
271 odom->pose.pose.position.z = 0.0;
272 odom->pose.pose.orientation = quat;
274 pose_cov_map.block<2, 2>(0, 0) << rpose_cov.block<2, 2>(0, 0);
275 pose_cov_map.block<2, 1>(0, 5) << rpose_cov.block<2, 1>(0, 2);
276 pose_cov_map.block<1, 2>(5, 0) << rpose_cov.block<1, 2>(2, 0);
277 pose_cov_map.block<1, 1>(5, 5) << rpose_cov.block<1, 1>(2, 2);
279 odom->twist = twist_cov;
285 if (tf_send && yaw_initialized) {
286 geometry_msgs::TransformStamped
transform;
288 transform.header.stamp = time;
292 transform.transform.translation.x =
rpose(0);
293 transform.transform.translation.y =
rpose(1);
294 transform.transform.translation.z = 0.0;
296 transform.transform.rotation = quat;
319 double y0 = wheel_offset[0](1);
320 double y1 = wheel_offset[1](1);
321 double a = -wheel_offset[0](0);
322 double dy_inv = 1.0 / (y1 - y0);
323 double dt_inv = 1.0 / dt;
326 double theta = (distance[1] - distance[0]) * dy_inv;
328 double L = (y1 * distance[0] - y0 * distance[1]) * dy_inv;
331 Eigen::Vector3d v(L, a*theta, theta);
343 double cos_theta = std::cos(theta);
344 double sin_theta = std::sin(theta);
347 if (std::abs(theta) > 1.
e-5) {
348 p = sin_theta / theta;
349 q = (1.0 - cos_theta) / theta;
364 Eigen::Vector3d dpose = M * v;
367 double cy = std::cos(
rpose(2));
368 double sy = std::sin(
rpose(2));
380 rtwist_cov(0) = vel_cov * (y0*y0 + y1*y1) * dy_inv*dy_inv;
381 rtwist_cov(1) = vel_cov * a*a * 2.0 * dy_inv*dy_inv + 0.001;
382 rtwist_cov(2) = vel_cov * 2.0 * dy_inv*dy_inv;
388 Eigen::Matrix3d R_yaw;
389 R_yaw << -sy, -cy, 0,
393 Eigen::Vector3d yaw_pose(0, 0, 1);
395 Eigen::Matrix3d J_pose = Eigen::Matrix3d::Identity() + R_yaw * dpose * yaw_pose.transpose();
398 double L_L0 = y1 * dy_inv;
399 double L_L1 = -y0 * dy_inv;
400 double theta_L0 = -dy_inv;
401 double theta_L1 = dy_inv;
403 Eigen::Matrix<double, 3, 2> v_meas;
404 v_meas << L_L0, L_L1,
405 a*theta_L0, a*theta_L1,
408 Eigen::Vector2d theta_meas(theta_L0, theta_L1);
412 if (std::abs(theta) > 1.
e-5) {
413 px = (theta*cos_theta - sin_theta) / (theta*theta);
414 qx = (theta*sin_theta - (1-cos_theta)) / (theta*theta);
422 Eigen::Matrix3d M_theta;
423 M_theta << px, -qx, 0,
427 Eigen::Matrix<double, 3, 2> J_meas = R * (M * v_meas + M_theta * v * theta_meas.transpose());
430 double L0_cov = vel_cov * dt*dt;
431 double L1_cov = vel_cov * dt*dt;
432 Eigen::Matrix2d meas_cov;
433 meas_cov << L0_cov, 0,
437 rpose_cov = J_pose * rpose_cov * J_pose.transpose() + J_meas * meas_cov * J_meas.transpose();
449 int nwheels = std::min(2, (
int)distance.size());
470 count_meas = measurement.size();
471 measurement_prev.resize(count_meas);
472 count = std::min(count, count_meas);
475 else if (time == time_prev) {
489 std::vector<double>
distance(std::max(2, count));
492 for (
int i = 0; i <
count; i++) {
493 double RPM_2_SPEED = wheel_radius[i] * 2.0 * M_PI / 60.0;
494 double rpm = 0.5 * (measurement[i] + measurement_prev[i]);
495 distance[i] = rpm * RPM_2_SPEED * dt;
500 for (
int i = 0; i <
count; i++)
501 distance[i] = measurement[i] - measurement_prev[i];
507 distance[1] = distance[0];
518 std::copy_n(measurement.begin(), measurement.size(), measurement_prev.begin());
529 void handle_rpm(
const mavlink::mavlink_message_t *msg, mavlink::ardupilotmega::msg::RPM &rpm)
536 auto rpm_msg = boost::make_shared<mavros_msgs::WheelOdomStamped>();
538 rpm_msg->header.stamp = timestamp;
539 rpm_msg->data.resize(2);
540 rpm_msg->data[0] = rpm.rpm1;
541 rpm_msg->data[1] = rpm.rpm2;
548 std::vector<double> measurement{rpm.rpm1, rpm.rpm2};
562 if (wheel_dist.count == 0)
568 ros::Time timestamp_int =
ros::Time(wheel_dist.time_usec / 1000000UL, 1000UL * (wheel_dist.time_usec % 1000000UL));
572 auto wheel_dist_msg = boost::make_shared<mavros_msgs::WheelOdomStamped>();
574 wheel_dist_msg->header.stamp = timestamp;
575 wheel_dist_msg->data.resize(wheel_dist.count);
576 std::copy_n(wheel_dist.distance.begin(), wheel_dist.count, wheel_dist_msg->data.begin());
578 dist_pub.
publish(wheel_dist_msg);
583 std::vector<double> measurement(wheel_dist.count);
584 std::copy_n(wheel_dist.distance.begin(), wheel_dist.count, measurement.begin());
void initialize(UAS &uas_)
Eigen::Vector3d rpose
Robot origin 2D-state (SI units)
bool twist_send
send geometry_msgs/TwistWithCovarianceStamped instead of nav_msgs/Odometry
OM odom_mode
odometry computation mode
int count_meas
number of wheels in measurements
#define ROS_INFO_NAMED(name,...)
std::string format(const std::string &fmt, Args...args)
#define ROS_WARN_THROTTLE_NAMED(period, name,...)
void publish(const boost::shared_ptr< M > &message) const
#define ROS_WARN_NAMED(name,...)
geometry_msgs::Quaternion get_attitude_orientation_enu()
void handle_rpm(const mavlink::mavlink_message_t *msg, mavlink::ardupilotmega::msg::RPM &rpm)
Handle Ardupilot RPM MAVlink message. Message specification: http://mavlink.io/en/messages/ardupilotm...
ros::Time time_prev
timestamp of previous measurement
HandlerInfo make_handler(const mavlink::msgid_t id, void(_C::*fn)(const mavlink::mavlink_message_t *msg, const mavconn::Framing framing))
use wheel's cumulative distance
Eigen::Map< Eigen::Matrix< double, 6, 6, Eigen::RowMajor > > EigenMapCovariance6d
std::vector< Eigen::Vector2d > wheel_offset
wheel x,y offsets (m,NED)
EIGEN_MAKE_ALIGNED_OPERATOR_NEW WheelOdometryPlugin()
TFSIMD_FORCE_INLINE tfScalar distance(const Vector3 &v) const
std::string tf_child_frame_id
frame for TF and Pose
std::string frame_id
origin frame for topic headers
tf2_ros::TransformBroadcaster tf2_broadcaster
bool raw_send
send wheel's RPM and cumulative distance
std::string tf_frame_id
origin for TF
std::vector< double > wheel_radius
wheel radiuses (m)
Eigen::Vector3d rtwist
twist (vx, vy, vyaw)
std::vector< double > measurement_prev
previous measurement
int count
requested number of wheels to compute odometry
void process_measurement(std::vector< double > measurement, bool rpm, ros::Time time, ros::Time time_pub)
Process wheel measurement.
bool param(const std::string ¶m_name, T ¶m_val, const T &default_val) const
ros::Time synchronise_stamp(uint32_t time_boot_ms)
Eigen::Matrix3d rpose_cov
pose error 1-var
void update_odometry_diffdrive(std::vector< double > distance, double dt)
Update odometry for differential drive robot. Odometry is computed for robot's origin (IMU)...
Eigen::Vector3d to_eigen(const geometry_msgs::Point r)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool yaw_initialized
initial yaw initialized (from IMU)
std::string child_frame_id
body-fixed frame for topic headers
void publish_odometry(ros::Time time)
Publish odometry. Odometry is computed from the very start but no pose info is published until we hav...
double vel_cov
wheel velocity measurement error 1-var (m/s)
Eigen::Quaterniond quaternion_from_rpy(const Eigen::Vector3d &rpy)
Eigen::Vector3d rtwist_cov
twist error 1-var (vx_cov, vy_cov, vyaw_cov)
std::vector< HandlerInfo > Subscriptions
void initialize(UAS &uas_)
double quaternion_get_yaw(const Eigen::Quaterniond &q)
void handle_wheel_distance(const mavlink::mavlink_message_t *msg, mavlink::common::msg::WHEEL_DISTANCE &wheel_dist)
Handle WHEEL_DISTANCE MAVlink message. Message specification: https://mavlink.io/en/messages/common.html#WHEEL_DISTANCE.
OM
Odometry computation modes.
bool hasParam(const std::string &key) const
sensor_msgs::Imu::Ptr get_attitude_imu_enu()
Subscriptions get_subscriptions()
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
void update_odometry(std::vector< double > distance, double dt)
Update odometry (currently, only 2-wheels differential configuration implemented). Odometry is computed for robot's origin (IMU).