一. 模块介绍
gnss_localizer模块主要负责将gps/rtk等传入的经纬高信息转换成xyz(需要指定原点),对于ndt定位算法起到一定的辅助作用。
二. 源码解析
关键输入输出:
// setup subscriber
sub1_ = nh_.subscribe("nmea_sentence", 100, &Nmea2TFPoseNode::callbackFromNmeaSentence, this);
// setup publisher
pub1_ = nh_.advertise<geometry_msgs::PoseStamped>("gnss_pose", 10);
关键回调函数callbackFromNmeaSentence:
void Nmea2TFPoseNode::callbackFromNmeaSentence(const nmea_msgs::Sentence::ConstPtr &msg)
{
current_time_ = msg->header.stamp;
// 根据当前帧输入的msg,从中解析出经纬高度信息,将其转换为xyz
//convert具体的解析函数
convert(split(msg->sentence), msg->header.stamp);
double timeout = 10.0;
// if orientation_stamp_ is 0 then no "QQ" sentence was ever received,
// so orientation should be computed from offsets
// 当从gps中无法获取方向信息/出现了较长时间段内没有获得方向信息
if (orientation_stamp_.isZero()
|| fabs(orientation_stamp_.toSec() - msg->header.stamp.toSec()) > timeout)
{
double dt = sqrt(pow(geo_.x() - last_geo_.x(), 2) + pow(geo_.y() - last_geo_.y(), 2));
double threshold = 0.2;
if (dt > threshold)
{
/* 第一帧不会pushlish
If orientation data is not available it is generated based on translation
from the previous position. For the first message the previous position is
simply the origin, which gives a wildly incorrect orientation. Some nodes
(e.g. ndt_matching) rely on that first message to initialise their pose guess,
and cannot recover from such incorrect orientation.
Therefore the first message is not published, ensuring that orientation is
only calculated from sensible positions.
*/
if (orientation_ready_)
{
ROS_INFO("QQ is not subscribed. Orientation is created by atan2");
//具体计算yaw函数
createOrientation();
publishPoseStamped();
publishTF();
}
else
{
orientation_ready_ = true;
}
last_geo_ = geo_;
}
return;
}
double e = 1e-2;
//要保证方向信息和位置信息的同步
if ((fabs(orientation_time_ - position_time_) < e) && orientation_ready_)
{
publishPoseStamped();
publishTF();
return;
}
}
具体计算yaw函数:
void Nmea2TFPoseNode::createOrientation()
{
yaw_ = atan2(geo_.x() - last_geo_.x(), geo_.y() - last_geo_.y());
roll_ = 0;
pitch_ = 0;
}
解析函数convert:
void Nmea2TFPoseNode::convert(std::vector<std::string> nmea, ros::Time current_stamp)
{
try
{
// 前两种字符串累计主要解析方向信息(nmea协议)
if (nmea.at(0).compare(0, 2, "QQ") == 0)
{
orientation_time_ = stod(nmea.at(3));
roll_ = stod(nmea.at(4)) * M_PI / 180.;
pitch_ = -1 * stod(nmea.at(5)) * M_PI / 180.;
yaw_ = -1 * stod(nmea.at(6)) * M_PI / 180. + M_PI / 2;
orientation_stamp_ = current_stamp;
orientation_ready_ = true;
ROS_INFO("QQ is subscribed.");
}
else if (nmea.at(0) == "$PASHR")
{
orientation_time_ = stod(nmea.at(1));
roll_ = stod(nmea.at(4)) * M_PI / 180.;
pitch_ = -1 * stod(nmea.at(5)) * M_PI / 180.;
yaw_ = -1 * stod(nmea.at(2)) * M_PI / 180. + M_PI / 2;
orientation_ready_ = true;
ROS_INFO("PASHR is subscribed.");
}
//后两种解析位置信息,经纬度转换xyz
else if (nmea.at(0).compare(3, 3, "GGA") == 0)
{
position_time_ = stod(nmea.at(1));
double lat = stod(nmea.at(2));
double lon = stod(nmea.at(4));
double h = stod(nmea.at(9));
if (nmea.at(3) == "S")
lat = -lat;
if (nmea.at(5) == "W")
lon = -lon;
//转换函数
geo_.set_llh_nmea_degrees(lat, lon, h);
ROS_INFO("GGA is subscribed.");
}
else if (nmea.at(0) == "$GPRMC")
{
position_time_ = stoi(nmea.at(1));
double lat = stod(nmea.at(3));
double lon = stod(nmea.at(5));
double h = 0.0;
if (nmea.at(4) == "S")
lat = -lat;
if (nmea.at(6) == "W")
lon = -lon;
geo_.set_llh_nmea_degrees(lat, lon, h);
ROS_INFO("GPRMC is subscribed.");
}
}
catch (const std::exception &e)
{
ROS_WARN_STREAM("Message is invalid : " << e.what());
}
}