autoware之轮式里程计计算

这部分代码主要是接收了底盘的can消息 然后计算一个轮速里程计。具体的:

1.can_status_translator节点

  // setup subscriber
  sub1_ = nh_.subscribe("can_info", 100, &CanStatusTranslatorNode::callbackFromCANInfo, this);
  sub2_ = nh_.subscribe("vehicle_status", 10, &CanStatusTranslatorNode::callbackFromVehicleStatus, this);

  // setup publisher
  pub1_ = nh_.advertise<geometry_msgs::TwistStamped>("can_velocity", 10);
  pub2_ = nh_.advertise<std_msgs::Float32>("linear_velocity_viz", 10);
  pub3_ = nh_.advertise<autoware_msgs::VehicleStatus>("vehicle_status", 10);

一方面,接收底盘消息can_info 进入回调函数callbackFromCANInfo,主要是发布了
vehicle_status消息内容

autoware_msgs::VehicleStatus vs;
  vs.header = msg->header;
  vs.tm = msg->tm;
  vs.drivemode = msg->devmode;  // I think devmode is typo in CANInfo...
  vs.steeringmode = msg->strmode;
  vs.gearshift = msg->driveshift;
  vs.speed = msg->speed;
  vs.drivepedal = msg->drivepedal;
  vs.brakepedal = msg->brakepedal;
  vs.angle = msg->angle;
  vs.lamp = 0;
  vs.light = msg->light;

  pub3_.publish(vs);

另一方面,订阅vehicle_status消息,进入到callbackFromVehicleStatus 进而通过计算发布了can_velocity和linear_velocity_viz两个消息
can_velocity消息

void CanStatusTranslatorNode::publishVelocity(const autoware_msgs::VehicleStatusConstPtr& msg)
{
  geometry_msgs::TwistStamped tw;
  tw.header = msg->header;

  // linear velocity
  tw.twist.linear.x = kmph2mps(msg->speed);  // km/h -> m/s

  // angular velocity
  tw.twist.angular.z = v_info_.convertSteeringAngleToAngularVelocity(kmph2mps(msg->speed), msg->angle);

  pub1_.publish(tw);
}

linear_velocity_viz消息

void CanStatusTranslatorNode::publishVelocityViz(const autoware_msgs::VehicleStatusConstPtr& msg)
{
  std_msgs::Float32 fl;
  fl.data = msg->speed;
  pub2_.publish(fl);
}

2.can_odometry节点

  // setup subscriber
  sub1_ = nh_.subscribe("vehicle_status", 10, &CanOdometryNode::callbackFromVehicleStatus, this);

  // setup publisher
  pub1_ = nh_.advertise<nav_msgs::Odometry>("/vehicle/odom", 10);

订阅了vehicle_status消息,进入到callbackFromVehicleStatus回调中,计算轮式里程计
里程计的计算用到了阿克曼转向模型:
在这里插入图片描述(相应的理论可查阅资料)

  double vx = kmph2mps(msg->speed);
  double vth = v_info_.convertSteeringAngleToAngularVelocity(kmph2mps(msg->speed), msg->angle);
  odom_.updateOdometry(vx, vth, msg->header.stamp);

  geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(odom_.th);

  // first, we'll publish the transform over tf
  /*geometry_msgs::TransformStamped odom_trans;
  odom_trans.header.stamp = msg->header.stamp;
  odom_trans.header.frame_id = "odom";
  odom_trans.child_frame_id = "base_link";

  odom_trans.transform.translation.x = odom_.x;
  odom_trans.transform.translation.y = odom_.y;
  odom_trans.transform.translation.z = 0.0;
  odom_trans.transform.rotation = odom_quat;

  // send the transform
  odom_broadcaster_.sendTransform(odom_trans);
  */

  // next, we'll publish the odometry message over ROS
  nav_msgs::Odometry odom;
  odom.header.stamp = msg->header.stamp;
  odom.header.frame_id = "odom";

  // set the position
  odom.pose.pose.position.x = odom_.x;
  odom.pose.pose.position.y = odom_.y;
  odom.pose.pose.position.z = 0.0;
  odom.pose.pose.orientation = odom_quat;

  // set the velocity
  odom.child_frame_id = "base_link";
  odom.twist.twist.linear.x = vx;
  odom.twist.twist.angular.z = vth;

  // publish the message
  pub1_.publish(odom);

这里需要注意的是车体坐标系下的角速度计算,输入是车体速度和车轮转向角。

  double vth = v_info_.convertSteeringAngleToAngularVelocity(kmph2mps(msg->speed), msg->angle);

具体的

  VehicleInfo()
  {
    is_stored = false;
    wheel_base = 0.0;//前后轮距 L
    minimum_turning_radius = 0.0;//最小转弯半径  R
    maximum_steering_angle = 0.0;
  }
  double convertSteeringAngleToAngularVelocity(const double cur_vel_mps, const double cur_angle_deg)  // rad/s
  {
    return is_stored ? tan(deg2rad(getCurrentTireAngle(cur_angle_deg))) * cur_vel_mps / wheel_base : 0;
  }
  double getCurrentTireAngle(const double angle_deg)  // steering [degree] -> tire [degree]
  {
    return is_stored ? angle_deg * getMaximumTireAngle() / maximum_steering_angle : 0;
  }
  double getMaximumTireAngle()  // degree
  {
    return is_stored ? rad2deg(asin(wheel_base / minimum_turning_radius)) : 0;
  }

里程计计算

  void updateOdometry(const double vx, const double vth, const ros::Time &cur_time)
  {
    if (stamp.sec == 0 && stamp.nsec == 0)
    {
      stamp = cur_time;
    }
    double dt = (cur_time - stamp).toSec();
    double delta_x = (vx * cos(th)) * dt;
    double delta_y = (vx * sin(th)) * dt;
    double delta_th = vth * dt;

    ROS_INFO("dt : %f delta (x y th) : (%f %f %f %f)", dt, delta_x, delta_y, delta_th);

    x += delta_x;
    y += delta_y;
    th += delta_th;
    stamp = cur_time;
  }

也可以通过阿克曼转向模型理论将其里程计运动模型写成如下形式

double dt=msg->Header.stamp.toSec()-last_time;
last_time=msg->Header.stamp.toSec();
if(abs(veh_info.steering_angle)>=0.01)
  beta=atan((dist_backwheel/(dist_backwheel+dist_frontwheel))*tan(veh_info.steering_angle*(M_PI/180)));//msg.steering_angle 弧度or 角度  这里需要的弧度
else
  beta=0.0;
if(veh_info.vehicle_speed >=1e-5 &&veh_info.gear_position==03)
{
  float vehicle_speed=veh_info.vehicle_speed*velocity_scale_;
  odom_out.v_x=vehicle_speed*cos(odom_out.theta+beta);
  odom_out.v_y=vehicle_speed*sin(odom_out.theta+beta);
  odom_out.v_theta=(vehicle_speed/dist_frontwheel)*sin(beta)*angle_scale_;

  odom_out.x+=odom_out.v_x*dt;
  odom_out.y+=odom_out.v_y*dt;
  odom_out.theta+=odom_out.v_theta*dt;
}
if(veh_info.vehicle_speed >1e-5&& veh_info.gear_position==04)
{
  float vehicle_speed=veh_info.vehicle_speed*velocity_scale_;
  odom_out.v_x=vehicle_speed*cos(odom_out.theta+beta);
  odom_out.v_y=vehicle_speed*sin(odom_out.theta+beta);
  odom_out.v_theta=(vehicle_speed/dist_frontwheel)*sin(beta)*angle_scale_;
  odom_out.x+=odom_out.v_x*dt;
  odom_out.y+=odom_out.v_y*dt;
  odom_out.theta+=odom_out.v_theta*dt;
}
geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(odom_out.theta);
geometry_msgs::TransformStamped odom_trans;
odom_trans.header.stamp =current_time;
odom_trans.header.frame_id = "map";
odom_trans.child_frame_id = "odom";

odom_trans.transform.translation.x = odom_out.x;
odom_trans.transform.translation.y = odom_out.y;
odom_trans.transform.translation.z = 0.0;
odom_trans.transform.rotation = odom_quat;
odom_broadcaster.sendTransform(odom_trans);

nav_msgs::Odometry odom;
odom.header.stamp = current_time;
odom.header.frame_id = "map";
odom.child_frame_id = "odom";
//set the position
odom.pose.pose.position.x = odom_out.x;
odom.pose.pose.position.y = odom_out.y;
odom.pose.pose.position.z = 0.0;
odom.pose.pose.orientation = odom_quat;

odom.twist.twist.linear.x =   odom_out.v_x;
odom.twist.twist.linear.y =   odom_out.v_y;
odom.twist.twist.angular.z =  odom_out.v_theta;
odom_pub_.publish(odom);
  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Authware古诗教程是一款专门为学习和欣赏古诗而设计的软件。它提供了丰富的古诗资源和相关的学习功能,帮助用户更好地理解和感受古代文化。 首先,Authware古诗教程拥有庞大的古诗库,涵盖了各个朝代的经典古诗。用户可以通过浏览功能,随时查阅自己喜欢的古诗,了解作者背景和作品的内涵。 其次,Authware古诗教程提供了详细的古诗解析和注释。对于初学者来说,读懂古诗可能会有一些难度,但通过软件提供的解析和注释,用户可以更好地理解古人的用词和意境,进一步欣赏古诗之美。 此外,Authware古诗教程还提供了古诗背景介绍和相关阅读材料。了解古代社会背景和文化氛围,能够更加全面地把握古诗的内涵和艺术价值。通过相关阅读材料的学习,用户可以拓展自己的知识面,提升古诗欣赏的层次。 最后,Authware古诗教程还提供了互动学习和分享社区。用户可以与其他热爱古诗的人交流学习,分享自己的感悟和见解。这种互动交流可以激发更多的灵感和思考,使学习和欣赏古诗变得更加有趣和有意义。 总之,Authware古诗教程是一款功能丰富、易于操作的软件,它可以帮助用户更好地学习和欣赏古诗,了解古代文化,并与其他人分享自己的想法。无论是初学者还是资深爱好者,都可以在这个平台上找到自己需要的资源和交流机会。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值