Initial commit

This commit is contained in:
2026-07-27 13:51:19 +08:00
commit 7bec56ca51
5408 changed files with 1126933 additions and 0 deletions
+220
View File
@@ -0,0 +1,220 @@
cmake_minimum_required(VERSION 3.0.2)
project(beitian-bg620)
## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
geometry_msgs
nmea_msgs
nav_msgs
roscpp
rospy
sensor_msgs
std_msgs
tf2
tf2_geometry_msgs
tf2_ros
)
find_package(PkgConfig)
pkg_check_modules(SERIAL libserial)
## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)
## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()
################################################
## Declare ROS messages, services and actions ##
################################################
## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
## * add a build_depend tag for "message_generation"
## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
## but can be declared for certainty nonetheless:
## * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
## * add "message_generation" and every package in MSG_DEP_SET to
## find_package(catkin REQUIRED COMPONENTS ...)
## * add "message_runtime" and every package in MSG_DEP_SET to
## catkin_package(CATKIN_DEPENDS ...)
## * uncomment the add_*_files sections below as needed
## and list every .msg/.srv/.action file to be processed
## * uncomment the generate_messages entry below
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
## Generate messages in the 'msg' folder
# add_message_files(
# FILES
# Message1.msg
# Message2.msg
# )
## Generate services in the 'srv' folder
# add_service_files(
# FILES
# Service1.srv
# Service2.srv
# )
## Generate actions in the 'action' folder
# add_action_files(
# FILES
# Action1.action
# Action2.action
# )
## Generate added messages and services with any dependencies listed here
# generate_messages(
# DEPENDENCIES
# geometry_msgs# nav_msgs# sensor_msgs# std_msgs# tf2_geometry_msgs
# )
################################################
## Declare ROS dynamic reconfigure parameters ##
################################################
## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
## * add "dynamic_reconfigure" to
## find_package(catkin REQUIRED COMPONENTS ...)
## * uncomment the "generate_dynamic_reconfigure_options" section below
## and list every .cfg file to be processed
## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
# cfg/DynReconf1.cfg
# cfg/DynReconf2.cfg
# )
###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
INCLUDE_DIRS include
LIBRARIES beitian-bg620
CATKIN_DEPENDS geometry_msgs nav_msgs roscpp rospy sensor_msgs std_msgs tf2 tf2_geometry_msgs tf2_ros nmea_msgs serial
# DEPENDS system_lib
)
###########
## Build ##
###########
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
include
${catkin_INCLUDE_DIRS}
)
## Declare a C++ library
# add_library(${PROJECT_NAME}
# src/${PROJECT_NAME}/MKT4052A_DRIVER.cpp
# )
## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/MKT4052A_DRIVER_node.cpp)
## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")
## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
# ${catkin_LIBRARIES}
# )
#############
## Install ##
#############
# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
catkin_install_python(PROGRAMS
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
## Mark executables for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
## Mark libraries for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )
## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
# FILES_MATCHING PATTERN "*.h"
# PATTERN ".svn" EXCLUDE
# )
## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
# # myfile1
# # myfile2
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )
#############
## Testing ##
#############
## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_MKT4052A_DRIVER.cpp)
# if(TARGET ${PROJECT_NAME}-test)
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()
## Add folders to be run by python nosetests
# catkin_add_nosetests(test)
add_executable(bg620_serial_reader_node src/bg620_serial_reader_node.cpp src/bg620_serial_reader_core.cpp)
target_link_libraries(bg620_serial_reader_node ${catkin_LIBRARIES} ${SERIAL_LDFLAGS})
add_executable(bg620_sentence_parse_node src/bg620_sentence_parse_node.cpp src/bg620_sentence_parse_core.cpp)
target_link_libraries(bg620_sentence_parse_node ${catkin_LIBRARIES})
+1
View File
@@ -0,0 +1 @@
该包为北天BG-620 RTK组合导航驱动包。
@@ -0,0 +1,106 @@
#ifndef BG620_SENTENCE_PARSE_H
#define BG620_SENTENCE_PARSE_H
#include "ros/ros.h"
#include "tf2_ros/buffer.h"
#include "tf2_ros/transform_listener.h"
#include "tf2_ros/transform_broadcaster.h"
#include <tf2_eigen/tf2_eigen.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include "geometry_msgs/TransformStamped.h"
#include "nmea_msgs/Sentence.h"
#include "sensor_msgs/Imu.h"
#include "nav_msgs/Path.h"
#include "tf/tf.h"
#include "Eigen/Core"
#include "Eigen/Geometry"
#include "gpsTools.hpp"
// Added by dawei
#include "sensor_msgs/NavSatFix.h"
#include "nav_msgs/Odometry.h"
#include "tf2_geometry_msgs/tf2_geometry_msgs.h"
#include "Eigen/Dense"
#define __APP_NAME__ "bg620_sentence_parse"
class BG620SentenceParse{
public:
BG620SentenceParse();
~BG620SentenceParse();
private:
ros::NodeHandle nh_;
ros::NodeHandle private_nh_;
tf2_ros::TransformListener listener;
tf2_ros::Buffer buffer;
private:
std::string outputNavSatFixTopic_;
std::string outputNavSatFixFrameId;
std::string outputOdometryTopic_;
std::string outputOdometryFrameId;
std::string outputLidarOdometryTopic_;
std::string outputLidarOdometryFrameId;
std::string outputTransFromStampedTopic_;
std::string outputTransFromStampedFrameId;
std::string outputImuTopic_;
std::string outputImuFrameId;
std::string outputGpsPathTopic_;
std::string robotOdomFrameId;
bool publishDynamicTf;
std::string inputNmeaSentenceTopic_;
double orinLon;
double orinLat;
double orinAlt;
double e_bias;
double n_bias;
private:
ros::Publisher navSatFixPublisher;
ros::Publisher odometryPublisher;
ros::Publisher lidarOdometryPublisher;
ros::Publisher transformStampedPublisher;
ros::Publisher imuPublisher;
ros::Publisher gpsPathPublisher;
ros::Subscriber nmeaSentenceSubscriber;
tf2_ros::TransformBroadcaster transformBroadcaster;
private:
GpsTools gpsTools;
nav_msgs::Path gpsPath;
private:
std::vector<std::string> splitStr(std::string str, const std::string &pattern);
void nmeaSentenceCallback(const nmea_msgs::Sentence::ConstPtr& msg);
void processBG620Data();
// Addd by Dawei
struct {
sensor_msgs::NavSatFix nav_fix;
double speed = 0.0;
double track_angle = 0.0;
double heading_deg = 0.0;
geometry_msgs::Quaternion orientation;
bool has_gga = false;
bool has_rmc = false;
bool has_heading = false;
ros::Time stamp;
} bg620_data_;
geometry_msgs::Quaternion current_orientation_;
double current_speed_;
Eigen::Quaternionf q;
// 新增辅助函数
double dmToDeg(double dm, char direction);
ros::Time parseUTCTime(const std::string& utc_time, const std::string& date = "");
double deg2rad(double deg) { return deg * M_PI / 180.0; }
};
#endif
@@ -0,0 +1,26 @@
#ifndef RTK_SERIAL_READER_JIAKAO_H
#define RTK_SERIAL_READER_JIAKAO_H
#include "ros/ros.h"
#include "nmea_msgs/Sentence.h"
#define __APP_NAME__ "rtk_serial_reader_bg620"
class RtkSerialReaderBG620{
public:
RtkSerialReaderBG620();
~RtkSerialReaderBG620();
void run();
private:
ros::NodeHandle nh_;
ros::NodeHandle private_nh_;
ros::Publisher sentence_publisher;
private:
std::string outPutSentenceTopic;
std::string gpsComChannel;
int gpsComBaudRate;
};
#endif
+143
View File
@@ -0,0 +1,143 @@
//
// Created by echo on 2019/11/26.
//
#ifndef PCD_COMPARE_GPSTOOLS_H
#define PCD_COMPARE_GPSTOOLS_H
#include <nav_msgs/Odometry.h>
#include <sensor_msgs/NavSatFix.h>
#include <boost/foreach.hpp>
#include <geometry_msgs/QuaternionStamped.h>
#include <Eigen/Core>
#define DEG_TO_RAD 0.01745329252
#define EARTH_MAJOR 6378137.0 ///< WGS84 MAJOR AXIS
#define EARTH_MINOR 6356752.31424518 ///< WGS84 MINOR AXIS
class GpsTools {
public:
GpsTools() { lla_origin_.setIdentity(); };
// Eigen::Vector3d LLA2ECEF(const Eigen::Vector3d &lla);
// Eigen::Vector3d ECEF2LLA(const Eigen::Vector3d &ecef);
// Eigen::Vector3d ECEF2ENU(const Eigen::Vector3d &ecef);
// Eigen::Vector3d ENU2ECEF(const Eigen::Vector3d &enu);
//static Eigen::Vector3d GpsMsg2Eigen(const sensor_msgs::NavSatFix &gps_msgs);
// void updateGPSpose(const sensor_msgs::NavSatFix &gps_msgs);
/**
* ros msg to eigen
* @param gps_msgs
* @return
*/
static Eigen::Vector3d GpsMsg2Eigen(const sensor_msgs::NavSatFix &gps_msgs) {
Eigen::Vector3d
lla(gps_msgs.latitude, gps_msgs.longitude, gps_msgs.altitude);
return lla;
}
/**
* //2. LLA经度(longitude),纬度(latitude)和高度(altitude)经纬高坐标系 转(Earth-Centered, Earth-Fixed)
* Z轴指向指向北,但不完全精确地与地球转动轴重合。转动轴有微小“摆动”,称之为“极运动(polar motion)”
* X轴在球面上与格林威治线和赤道的交点
* @param lla
* @return
*/
Eigen::Vector3d LLA2ECEF(const Eigen::Vector3d &lla) {
Eigen::Vector3d ecef;
double lat = deg2rad(lla.x());
double lon = deg2rad(lla.y());
double alt = lla.z();
double earth_r = pow(EARTH_MAJOR, 2)
/ sqrt(pow(EARTH_MAJOR * cos(lat), 2) + pow(EARTH_MINOR * sin(lat), 2));
ecef.x() = (earth_r + alt) * cos(lat) * cos(lon);
ecef.y() = (earth_r + alt) * cos(lat) * sin(lon);
ecef.z() = (pow(EARTH_MINOR / EARTH_MAJOR, 2) * earth_r + alt) * sin(lat);
return ecef;
}
Eigen::Vector3d ECEF2LLA(const Eigen::Vector3d &ecef) {
double e =
sqrt((pow(EARTH_MAJOR, 2) - pow(EARTH_MINOR, 2)) / pow(EARTH_MAJOR, 2));
double e_ =
sqrt((pow(EARTH_MAJOR, 2) - pow(EARTH_MINOR, 2)) / pow(EARTH_MINOR, 2));
double p = sqrt(pow(ecef.x(), 2) + pow(ecef.y(), 2));
double theta = atan2(ecef.z() * EARTH_MAJOR, p * EARTH_MINOR);
double lon = atan2(ecef.y(), ecef.x());
double lat = atan2((ecef.z() + pow(e_, 2) * EARTH_MINOR * pow(sin(theta), 3)),
p - pow(e, 2) * EARTH_MAJOR * pow(cos(theta), 3));
double earth_r = pow(EARTH_MAJOR, 2)
/ sqrt(pow(EARTH_MAJOR * cos(lat), 2) + pow(EARTH_MINOR * sin(lat), 2));
double alt = p / cos(lat) - earth_r;
Eigen::Vector3d lla(rad2deg(lat), rad2deg(lon), alt);
return lla;
}
Eigen::Vector3d ECEF2ENU(const Eigen::Vector3d &ecef) {
double lat = deg2rad(lla_origin_.x());
double lon = deg2rad(lla_origin_.y());
Eigen::Vector3d t = -LLA2ECEF(lla_origin_);
Eigen::Matrix3d r;
r << -sin(lon), cos(lon), 0,
-cos(lon) * sin(lat), -sin(lat) * sin(lon), cos(lat),
cos(lon) * cos(lat), sin(lon) * cos(lat), sin(lat);
Eigen::Vector3d enu;
enu = ecef + t;
enu = r * enu;
return enu;
}
Eigen::Vector3d ENU2ECEF(const Eigen::Vector3d &enu) {
double lat = deg2rad(lla_origin_.x());
double lon = deg2rad(lla_origin_.y());
Eigen::Vector3d t = LLA2ECEF(lla_origin_);
Eigen::Matrix3d r;
r << -sin(lon), -cos(lon) * sin(lat), cos(lon) * cos(lat),
cos(lon), -sin(lon) * sin(lat), sin(lon) * cos(lat),
0, cos(lat), sin(lat);
Eigen::Vector3d ecef;
ecef = r * enu + t;
return ecef;
}
void updateGPSpose(const sensor_msgs::NavSatFix &gps_msgs) {
//检查状态4
if (gps_msgs.status.status == 4 || gps_msgs.status.status == 5 || gps_msgs.status.status == 1
|| gps_msgs.status.status == 2) {
//第一个的时候设置为起点
if (lla_origin_ == Eigen::Vector3d::Identity()) {
Eigen::Vector3d lla = GpsMsg2Eigen(gps_msgs);
lla_origin_ = lla;
std::cout << "GPS origin: " << lla_origin_ << "\n status: " << gps_msgs.status.status << std::endl;
} else {
Eigen::Vector3d lla = GpsMsg2Eigen(gps_msgs);
Eigen::Vector3d ecef = LLA2ECEF(lla);
Eigen::Vector3d enu = ECEF2ENU(ecef);
gps_pos_ = enu;
std::cout << "GPS lla_origin_: " << lla_origin_ << "\n curr" << gps_pos_ << std::endl;
}
}
}
//变量部分
//1.lla的起点
Eigen::Vector3d lla_origin_;
//2.enu下的坐标
Eigen::Vector3d gps_pos_;
private:
static inline double deg2rad(const double &deg) {
return deg * DEG_TO_RAD;
};
static inline double rad2deg(const double &rad) {
return rad / DEG_TO_RAD;
}
};
#endif //PCD_COMPARE_GPSTOOLS_H
@@ -0,0 +1,4 @@
<launch>
<include file="$(find beitian-bg620)/launch/include/beitian_bg620_serial_reader.launch" />
<include file="$(find beitian-bg620)/launch/include/beitian_bg620_sentence_parse.launch" />
</launch>
@@ -0,0 +1,69 @@
<launch>
<arg name="outputNavSatFixTopic_" default="/bg620/gpsFix"/>
<arg name="outputNavSatFixFrameId" default="gps"/>
<arg name="outputOdometryTopic_" default="/bg620/gpsOdom"/>
<arg name="outputOdometryFrameId" default="gps"/>
<arg name="outputLidarOdometryTopic_" default="/bg620/lidarOdom"/>
<arg name="outputLidarOdometryFrameId" default="c32_L"/>
<arg name="outputTransFromStampedTopic_" default="/bg620/gpsTransFromStamped"/>
<arg name="outputTransFromStampedFrameId" default="gps"/>
<arg name="outputImuTopic_" default="/bg620/gpsImu"/>
<arg name="outputImuFrameId" default="gps"/>
<arg name="outputGpsPathTopic_" default="/bg620/gpsPath"/>
<arg name="robotOdomFrameId" default="base_link"/>
<arg name="inputNmeaSentenceTopic_" default="/bg620/nmeaSentence"/>
<arg name="publishDynamicTf" default="false"/>
<!-- origin of dd_yuanqu -->
<!-- <arg name="orinLon" default="116.178987"/>
<arg name="orinLat" default="39.903197"/>
<arg name="orinAlt" default="75.0848"/> -->
<!-- origin of haidian_park -->
<arg name="orinLon" default="116.2899854448"/>
<arg name="orinLat" default="39.98534"/>
<arg name="orinAlt" default="48.389"/>
<!-- origin of caihonghu_park -->
<!-- <arg name="orinLon" default="117.2874076873333"/>
<arg name="orinLat" default="36.67463832666667"/>
<arg name="orinAlt" default="115.1813"/> -->
<arg name="e_bias" default="0.0"/>
<arg name="n_bias" default="0.0"/>
<node name="bg620_sentence_parse_node" pkg="beitian-bg620" type="bg620_sentence_parse_node" output="screen">
<param name="outputNavSatFixTopic_" value="$(arg outputNavSatFixTopic_)" type="string"/>
<param name="outputNavSatFixFrameId" value="$(arg outputNavSatFixFrameId)" type="string"/>
<param name="outputOdometryTopic_" value="$(arg outputOdometryTopic_)" type="string"/>
<param name="outputOdometryFrameId" value="$(arg outputOdometryFrameId)" type="string"/>
<param name="outputLidarOdometryTopic_" value="$(arg outputLidarOdometryTopic_)" type="string"/>
<param name="outputLidarOdometryFrameId" value="$(arg outputLidarOdometryFrameId)" type="string"/>
<param name="outputTransFromStampedTopic_" value="$(arg outputTransFromStampedTopic_)" type="string"/>
<param name="outputTransFromStampedFrameId" value="$(arg outputTransFromStampedFrameId)" type="string"/>
<param name="outputImuTopic_" value="$(arg outputImuTopic_)" type="string"/>
<param name="outputImuFrameId" value="$(arg outputImuFrameId)" type="string"/>
<param name="outputGpsPathTopic_" value="$(arg outputGpsPathTopic_)" type="string"/>
<param name="robotOdomFrameId" value="$(arg robotOdomFrameId)" type="string"/>
<param name="inputNmeaSentenceTopic_" value="$(arg inputNmeaSentenceTopic_)" type="string"/>
<param name="publishDynamicTf" value="$(arg publishDynamicTf)" type="bool"/>
<param name="orinLon" value="$(arg orinLon)" type="double"/>
<param name="orinLat" value="$(arg orinLat)" type="double"/>
<param name="orinAlt" value="$(arg orinAlt)" type="double"/>
<param name="e_bias" value="$(arg e_bias)" type="double"/>
<param name="n_bias" value="$(arg n_bias)" type="double"/>
</node>
</launch>
@@ -0,0 +1,11 @@
<launch>
<arg name="outPutSentenceTopic" default="/bg620/nmeaSentence"/>
<arg name="gpsComChannel" default="/dev/ttyACM0"/>
<arg name="gpsComBaudRate" default="115200"/>
<node name="bg620_serial_reader_node" pkg="beitian-bg620" type="bg620_serial_reader_node" output="screen">
<param name="outPutSentenceTopic" value="$(arg outPutSentenceTopic)" type="string"/>
<param name="gpsComChannel" value="$(arg gpsComChannel)" type="string"/>
<param name="gpsComBaudRate" value="$(arg gpsComBaudRate)" type="int"/>
</node>
</launch>
+97
View File
@@ -0,0 +1,97 @@
<?xml version="1.0"?>
<package format="2">
<name>beitian-bg620</name>
<version>0.0.0</version>
<description>The beitian_bg620 package</description>
<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<maintainer email="buaazs17@163.com">zhangshu</maintainer>
<!-- One license tag required, multiple allowed, one license per tag -->
<!-- Commonly used license strings: -->
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
<license>TODO</license>
<!-- Url tags are optional, but multiple are allowed, one per tag -->
<!-- Optional attribute type can be: website, bugtracker, or repository -->
<!-- Example: -->
<!-- <url type="website">http://wiki.ros.org/newton-m2</url> -->
<!-- Author tags are optional, multiple are allowed, one per tag -->
<!-- Authors do not have to be maintainers, but could be -->
<!-- Example: -->
<!-- <author email="jane.doe@example.com">Jane Doe</author> -->
<!-- The *depend tags are used to specify dependencies -->
<!-- Dependencies can be catkin packages or system dependencies -->
<!-- Examples: -->
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
<!-- <depend>roscpp</depend> -->
<!-- Note that this is equivalent to the following: -->
<!-- <build_depend>roscpp</build_depend> -->
<!-- <exec_depend>roscpp</exec_depend> -->
<!-- Use build_depend for packages you need at compile time: -->
<!-- <build_depend>message_generation</build_depend> -->
<!-- Use build_export_depend for packages you need in order to build against this package: -->
<!-- <build_export_depend>message_generation</build_export_depend> -->
<!-- Use buildtool_depend for build tool packages: -->
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<!-- Use exec_depend for packages you need at runtime: -->
<!-- <exec_depend>message_runtime</exec_depend> -->
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<!-- Use doc_depend for packages you need only for building documentation: -->
<!-- <doc_depend>doxygen</doc_depend> -->
<buildtool_depend>catkin</buildtool_depend>
<build_depend>geometry_msgs</build_depend>
<build_depend>nav_msgs</build_depend>
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>sensor_msgs</build_depend>
<build_depend>std_msgs</build_depend>
<build_depend>tf2</build_depend>
<build_depend>tf2_geometry_msgs</build_depend>
<build_depend>tf2_ros</build_depend>
<build_depend>nmea_msgs</build_depend>
<build_depend>serial</build_depend>
<build_export_depend>geometry_msgs</build_export_depend>
<build_export_depend>nav_msgs</build_export_depend>
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>rospy</build_export_depend>
<build_export_depend>sensor_msgs</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<build_export_depend>tf2</build_export_depend>
<build_export_depend>tf2_geometry_msgs</build_export_depend>
<build_export_depend>tf2_ros</build_export_depend>
<build_export_depend>nmea_msgs</build_export_depend>
<build_export_depend>serial</build_export_depend>
<exec_depend>geometry_msgs</exec_depend>
<exec_depend>nav_msgs</exec_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>sensor_msgs</exec_depend>
<exec_depend>std_msgs</exec_depend>
<exec_depend>tf2</exec_depend>
<exec_depend>tf2_geometry_msgs</exec_depend>
<exec_depend>tf2_ros</exec_depend>
<exec_depend>nmea_msgs</exec_depend>
<exec_depend>serial</exec_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->
</export>
</package>
@@ -0,0 +1,381 @@
#include "bg620_sentence_parse_core.h"
BG620SentenceParse::BG620SentenceParse():nh_(""), private_nh_("~"), listener(buffer), current_speed_(0.0){
// 参数初始化
private_nh_.param<std::string>("outputNavSatFixTopic_", outputNavSatFixTopic_, "");
private_nh_.param<std::string>("outputNavSatFixFrameId", outputNavSatFixFrameId, "");
// [Debug] Print parameters
ROS_INFO("[BG620] Parameters loaded:");
ROS_INFO("outputNavSatFixTopic: %s", outputNavSatFixTopic_.c_str());
ROS_INFO("outputOdometryTopic: %s", outputOdometryTopic_.c_str());
ROS_INFO("inputNmeaSentenceTopic_: %s", inputNmeaSentenceTopic_.c_str());
private_nh_.param<std::string>("outputOdometryTopic_", outputOdometryTopic_, "");
private_nh_.param<std::string>("outputOdometryFrameId", outputOdometryFrameId, "");
private_nh_.param<std::string>("outputLidarOdometryTopic_", outputLidarOdometryTopic_, "");
private_nh_.param<std::string>("outputLidarOdometryFrameId", outputLidarOdometryFrameId, "");
private_nh_.param<std::string>("outputTransFromStampedTopic_", outputTransFromStampedTopic_, "");
private_nh_.param<std::string>("outputTransFromStampedFrameId", outputTransFromStampedFrameId, "");
private_nh_.param<std::string>("outputImuTopic_", outputImuTopic_, "");
private_nh_.param<std::string>("outputImuFrameId", outputImuFrameId, "");
private_nh_.param<std::string>("outputGpsPathTopic_", outputGpsPathTopic_, "");
private_nh_.param<std::string>("robotOdomFrameId", robotOdomFrameId, "");
private_nh_.param<std::string>("inputNmeaSentenceTopic_", inputNmeaSentenceTopic_, "");
private_nh_.param<bool>("publishDynamicTf", publishDynamicTf, false);
private_nh_.param<double>("orinLon", orinLon, 0.0);
private_nh_.param<double>("orinLat", orinLat, 0.0);
private_nh_.param<double>("orinAlt", orinAlt, 0.0);
private_nh_.param<double>("e_bias", e_bias, 0.0);
private_nh_.param<double>("n_bias", n_bias, 0.0);
// 初始化发布器
imuPublisher = nh_.advertise<sensor_msgs::Imu>(outputImuTopic_, 1);
navSatFixPublisher = nh_.advertise<sensor_msgs::NavSatFix>(outputNavSatFixTopic_, 1);
odometryPublisher = nh_.advertise<nav_msgs::Odometry>(outputOdometryTopic_, 1);
lidarOdometryPublisher = nh_.advertise<nav_msgs::Odometry>(outputLidarOdometryTopic_, 1);
transformStampedPublisher = nh_.advertise<geometry_msgs::TransformStamped>(outputTransFromStampedTopic_, 1);
gpsPathPublisher = nh_.advertise<nav_msgs::Path>(outputGpsPathTopic_, 1);
ROS_INFO("[BG620] Publishers initialized");
// 初始化订阅器
nmeaSentenceSubscriber = nh_.subscribe<nmea_msgs::Sentence>(inputNmeaSentenceTopic_, 1, &BG620SentenceParse::nmeaSentenceCallback, this);
// 初始化GPS工具
gpsTools.lla_origin_<<orinLat, orinLon, orinAlt;
}
BG620SentenceParse::~BG620SentenceParse(){
}
// 度分转换实现
double BG620SentenceParse::dmToDeg(double dm, char direction) {
int degree = static_cast<int>(dm / 100);
double minutes = dm - degree * 100;
double decimal_deg = degree + minutes / 60.0;
return (direction == 'S' || direction == 'W') ? -decimal_deg : decimal_deg;
}
// UTC时间解析实现
ros::Time BG620SentenceParse::parseUTCTime(const std::string& utc_time, const std::string& date) {
try {
// 如果没有提供日期,使用当前日期
std::string use_date = date.empty() ? "010100" : date; // 默认2000-01-01
int day = std::stoi(use_date.substr(0,2));
int month = std::stoi(use_date.substr(2,2));
int year = 2000 + std::stoi(use_date.substr(4,2));
int hours = std::stoi(utc_time.substr(0,2));
int minutes = std::stoi(utc_time.substr(2,2));
int seconds = std::stoi(utc_time.substr(4,2));
int nsec = 0;
if (utc_time.length() > 6 && utc_time[6] == '.') {
double frac_sec = std::stod("0." + utc_time.substr(7));
nsec = static_cast<int>(frac_sec * 1e9);
}
tm time_struct = {};
time_struct.tm_year = year - 1900;
time_struct.tm_mon = month - 1;
time_struct.tm_mday = day;
time_struct.tm_hour = hours;
time_struct.tm_min = minutes;
time_struct.tm_sec = seconds;
time_t time_utc = timegm(&time_struct);
return ros::Time(time_utc, nsec);
} catch (...) {
ROS_WARN("Failed to parse UTC time: %s", utc_time.c_str());
return ros::Time::now();
}
}
// 字符串分割实现
std::vector<std::string> BG620SentenceParse::splitStr(std::string str, const std::string &pattern){
std::string::size_type pos;
std::vector<std::string> result;
str += pattern;
int size = str.size();
for (int i = 0; i < size; i++)
{
pos = str.find(pattern, i);
if (pos < size)
{
std::string s = str.substr(i, pos - i);
result.push_back(s);
i = pos + pattern.size() - 1;
}
}
return result;
}
// 核心回调函数
void BG620SentenceParse::nmeaSentenceCallback(const nmea_msgs::Sentence::ConstPtr& msg){
try{
// ROS_DEBUG("[BG620] Received NMEA: %s", msg->sentence.c_str());
std::string raw_msg = msg->sentence;
// 统一分隔符处理
std::vector<std::string> fields = splitStr(raw_msg.substr(0, raw_msg.find("*")), ",");
if (fields.empty()) {
ROS_WARN("[BG620] Empty NMEA message");
return;
}
// GNGGA消息处理(第一条)
if (fields[0] == "$GNGGA") {
// ROS_DEBUG("[BG620] Processing GNGGA message");
bg620_data_.has_gga = true;
bg620_data_.stamp = msg->header.stamp;
// 解析经纬度(度分格式)
bg620_data_.nav_fix.latitude = dmToDeg(std::stod(fields[2]), fields[3][0]);
bg620_data_.nav_fix.longitude = dmToDeg(std::stod(fields[4]), fields[5][0]);
bg620_data_.nav_fix.altitude = std::stod(fields[9]);
// 设置状态
bg620_data_.nav_fix.status.service = sensor_msgs::NavSatStatus::SERVICE_GPS;
int gps_status = std::stoi(fields[6]); // 先将字段转换为整数
bg620_data_.nav_fix.status.status = gps_status;
// ROS_INFO("[BG620] GPS - Lat: %.6f, Lon: %.6f, Alt: %.2f",
// bg620_data_.nav_fix.latitude,
// bg620_data_.nav_fix.longitude,
// bg620_data_.nav_fix.altitude);
}
// // GNRMC消息处理(第二条)
// else if (fields[0] == "$GNRMC") {
//// ROS_DEBUG("[BG620] Processing GNRMC message");
// bg620_data_.has_rmc = true;
// bg620_data_.speed = std::stod(fields[7]) * 0.514444; // 节→m/s
// bg620_data_.track_angle = std::stod(fields[8]); // 真北航向角
//
// ROS_INFO("[BG620] Speed - %.2f m/s, Track: %.2f°",
// bg620_data_.speed,
// bg620_data_.track_angle);
// }
// HEADINGA消息处理(第三条)
else if (fields[0] == "#HEADINGA") {
// ROS_DEBUG("[BG620] Processing HEADINGA message");
bg620_data_.has_heading = true;
// 解析姿态角(单位:度)
double yaw = std::stod(fields[11]);
double pitch = std::stod(fields[12]);
double roll = std::stod(fields[13]);
// acturally fields[12] is the heading angle of rtk
bg620_data_.heading_deg = pitch;
double yaw_rad = yaw * M_PI / 180.0;
double pitch_rad = pitch * M_PI / 180.0;
double roll_rad = roll * M_PI / 180.0;
// 转换为四元数
geometry_msgs::Quaternion q;
q = tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw);
bg620_data_.orientation = q;
// 三条消息都收到后处理
// if (bg620_data_.has_gga && bg620_data_.has_rmc && bg620_data_.has_heading) {
if (bg620_data_.has_gga && bg620_data_.has_heading) {
// ROS_INFO("[BG620] All required messages received, processing data");
processBG620Data();
// 重置标志位
bg620_data_.has_gga = false;
// bg620_data_.has_rmc = false;
bg620_data_.has_heading = false;
}
// ROS_INFO("[BG620] Orien - Yaw: %.2f, Pitch: %.2f, Roll: %.2f",
// std::stod(fields[11]),
// std::stod(fields[12]),
// std::stod(fields[13]));
}
} catch (std::exception& e) {
ROS_ERROR("NMEA parsing error [%s]: %s", __APP_NAME__, e.what());
}
}
// 新增的统一处理函数
void BG620SentenceParse::processBG620Data() {
// 设置消息头
const ros::Time current_frame_time = ros::Time::now();
bg620_data_.nav_fix.header.stamp = current_frame_time;
bg620_data_.nav_fix.header.frame_id = outputNavSatFixFrameId;
// 发布NavSatFix
// ROS_INFO("[BG620] Publishing NavSatFix");
navSatFixPublisher.publish(bg620_data_.nav_fix);
// 发布里程计
nav_msgs::Odometry gpsOdometry;
gpsOdometry.header.frame_id = "map";
gpsOdometry.header.stamp = current_frame_time;
gpsOdometry.child_frame_id = outputOdometryFrameId;
// 坐标系转换
Eigen::Vector3d lla = gpsTools.GpsMsg2Eigen(bg620_data_.nav_fix);
Eigen::Vector3d ecef = gpsTools.LLA2ECEF(lla);
Eigen::Vector3d enu = gpsTools.ECEF2ENU(ecef);
// ROS_INFO("[BG620] ENU - E: %.2f, N: %.2f, U: %.2f",
// enu[0], enu[1], enu[2]);
gpsOdometry.pose.pose.position.x = enu[0]+e_bias;
gpsOdometry.pose.pose.position.y = enu[1]+n_bias;
gpsOdometry.pose.pose.position.z = enu[2];
// ROS_INFO("E=%.4f, N=%.4f, U=%.4f", enu[0],enu[1],enu[2]);
gpsOdometry.pose.pose.orientation.x = bg620_data_.orientation.x;
gpsOdometry.pose.pose.orientation.y = bg620_data_.orientation.y;
gpsOdometry.pose.pose.orientation.z = bg620_data_.orientation.z;
gpsOdometry.pose.pose.orientation.w = bg620_data_.orientation.w;
// just a trick to transpose solution status and yaw_angle
gpsOdometry.pose.covariance[1] = bg620_data_.nav_fix.status.status;
gpsOdometry.pose.covariance[2] = bg620_data_.heading_deg;
// 设置协方差
switch (bg620_data_.nav_fix.status.status) {
case 1: // 单点定位
gpsOdometry.pose.covariance[0] = 1.0; // x (m^2)
gpsOdometry.pose.covariance[7] = 1.0; // y (m^2)
gpsOdometry.pose.covariance[14] = 10.0; // z (m^2)
break;
case 2: // 差分定位
gpsOdometry.pose.covariance[0] = 0.5; // x (m^2)
gpsOdometry.pose.covariance[7] = 0.5; // y (m^2)
gpsOdometry.pose.covariance[14] = 5.0; // z (m^2)
break;
case 4: // RTK固定解
gpsOdometry.pose.covariance[0] = 0.01; // x (m^2)
gpsOdometry.pose.covariance[7] = 0.01; // y (m^2)
gpsOdometry.pose.covariance[14] = 0.1; // z (m^2)
break;
case 5: // RTK浮点解
gpsOdometry.pose.covariance[0] = 0.1; // x (m^2)
gpsOdometry.pose.covariance[7] = 0.1; // y (m^2)
gpsOdometry.pose.covariance[14] = 1.0; // z (m^2)
break;
case 6: // 惯导模式
gpsOdometry.pose.covariance[0] = 0.2; // x (m^2)
gpsOdometry.pose.covariance[7] = 0.2; // y (m^2)
gpsOdometry.pose.covariance[14] = 2.0; // z (m^2)
break;
default: // 其他状态 0,3,7,8 等(含无效状态)
gpsOdometry.pose.covariance[0] = 1000.0;
gpsOdometry.pose.covariance[7] = 1000.0;
gpsOdometry.pose.covariance[14] = 1000.0;
}
// 暂时不发布里程计的速度信息
// ROS_INFO("[BG620] Publishing gpsOdometry");
odometryPublisher.publish(gpsOdometry);
/* 发布geometry_msgs::TransFromed,用于标定组合导航与激光雷达的外参 */
geometry_msgs::TransformStamped ts;
ts.header.stamp = current_frame_time;
ts.header.frame_id = "map";
ts.child_frame_id = outputTransFromStampedFrameId;
ts.transform.translation.x = enu[0]+e_bias;
ts.transform.translation.y = enu[1]+n_bias;
ts.transform.translation.z = enu[2];
ts.transform.rotation.x = bg620_data_.orientation.x;
ts.transform.rotation.y = bg620_data_.orientation.y;
ts.transform.rotation.z = bg620_data_.orientation.z;
ts.transform.rotation.w = bg620_data_.orientation.w;
// ROS_INFO("[BG620] Publishing ts");
transformStampedPublisher.publish(ts);
// 发布Path 测试gps性能时用,部署时注释
gpsPath.header.frame_id = "map";
gpsPath.header.stamp = current_frame_time;
geometry_msgs::PoseStamped pose;
if(gpsPath.poses.size()>=600){
gpsPath.poses.clear();
}
pose.header = gpsPath.header;
pose.pose.position.x = enu[0]+e_bias;
pose.pose.position.y = enu[1]+n_bias;
pose.pose.position.z = enu[2];
pose.pose.orientation.x = bg620_data_.orientation.x;
pose.pose.orientation.y = bg620_data_.orientation.y;
pose.pose.orientation.z = bg620_data_.orientation.z;
pose.pose.orientation.w = bg620_data_.orientation.w;
gpsPath.poses.push_back(pose);
// ROS_INFO("[BG620] Publishing gpsPath");
gpsPathPublisher.publish(gpsPath);
Eigen::Affine3f transform;
Eigen::Matrix4f mat_pose;
geometry_msgs::TransformStamped map_wrt_frame;
geometry_msgs::TransformStamped frame_wrt_odom;
Eigen::Matrix4f frame2odom;
geometry_msgs::TransformStamped map_wrt_odom;
tf2::Transform odom_wrt_map;
geometry_msgs::TransformStamped odom_trans;
/* 生成4阶矩阵 */
transform = Eigen::Affine3f::Identity();
transform.translation() << enu[0]+e_bias, enu[1]+n_bias, enu[2];
Eigen::Quaternionf q(bg620_data_.orientation.w,
bg620_data_.orientation.x,
bg620_data_.orientation.y,
bg620_data_.orientation.z);
transform.rotate(q);
mat_pose = transform.matrix();
map_wrt_frame = tf2::eigenToTransform(Eigen::Isometry3d(mat_pose.inverse().cast<double>()));
map_wrt_frame.header.stamp = current_frame_time;
map_wrt_frame.header.frame_id = outputOdometryFrameId;
map_wrt_frame.child_frame_id = "map";
/* 发布激光里程计处的ODOM*/
nav_msgs::Odometry lidar_odom;
lidar_odom.header.frame_id = "map";
lidar_odom.header.stamp = current_frame_time;
lidar_odom.child_frame_id = outputLidarOdometryFrameId;
frame_wrt_odom = buffer.lookupTransform(outputLidarOdometryFrameId, outputOdometryFrameId, ros::Time(0));
frame2odom = tf2::transformToEigen(frame_wrt_odom).cast<float>().matrix();
tf2::doTransform(map_wrt_frame, map_wrt_odom, frame_wrt_odom);
tf2::fromMsg(map_wrt_odom.transform, odom_wrt_map);
odom_wrt_map = odom_wrt_map.inverse();
odom_trans.transform = tf2::toMsg(odom_wrt_map);
lidar_odom.pose.pose.position.x = odom_trans.transform.translation.x;
lidar_odom.pose.pose.position.y = odom_trans.transform.translation.y;
lidar_odom.pose.pose.position.z = odom_trans.transform.translation.z;
lidar_odom.pose.pose.orientation.x = odom_trans.transform.rotation.x;
lidar_odom.pose.pose.orientation.y = odom_trans.transform.rotation.y;
lidar_odom.pose.pose.orientation.z = odom_trans.transform.rotation.z;
lidar_odom.pose.pose.orientation.w = odom_trans.transform.rotation.w;
// ROS_INFO("[BG620] PUblishing lidar_odom");
lidarOdometryPublisher.publish(lidar_odom);
// 发布动态TF
if(publishDynamicTf && bg620_data_.nav_fix.status.status == sensor_msgs::NavSatStatus::STATUS_GBAS_FIX){
frame_wrt_odom = buffer.lookupTransform(robotOdomFrameId, outputOdometryFrameId, ros::Time(0));
frame2odom = tf2::transformToEigen(frame_wrt_odom).cast<float>().matrix();
tf2::doTransform(map_wrt_frame, map_wrt_odom, frame_wrt_odom);
tf2::fromMsg(map_wrt_odom.transform, odom_wrt_map);
odom_wrt_map = odom_wrt_map.inverse();
odom_trans.transform = tf2::toMsg(odom_wrt_map);
odom_trans.header.stamp = current_frame_time;
odom_trans.header.frame_id = "map";
odom_trans.child_frame_id = robotOdomFrameId;
transformBroadcaster.sendTransform(odom_trans);
}
}
@@ -0,0 +1,9 @@
#include "bg620_sentence_parse_core.h"
int main(int argc, char **argv){
setlocale(LC_CTYPE, "zh_CN.utf8");
ros::init(argc, argv, "bg620_nmea_sentence_parse");
BG620SentenceParse bg620SentenceParse;
ros::spin();
return 0;
}
@@ -0,0 +1,72 @@
#include "bg620_serial_reader_core.h"
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <libserial/SerialPort.h>
RtkSerialReaderBG620::RtkSerialReaderBG620():nh_(""), private_nh_("~"){
private_nh_.param<std::string>("outPutSentenceTopic", outPutSentenceTopic, "");
private_nh_.param<std::string>("gpsComChannel", gpsComChannel, "");
private_nh_.param<int>("gpsComBaudRate", gpsComBaudRate, 115200);
}
RtkSerialReaderBG620::~RtkSerialReaderBG620(){
}
void RtkSerialReaderBG620::run(){
sentence_publisher = nh_.advertise<nmea_msgs::Sentence>(outPutSentenceTopic, 1);
LibSerial::SerialPort serial_port;
try {
serial_port.Open(gpsComChannel);
switch (gpsComBaudRate) {
case 9600:
serial_port.SetBaudRate(LibSerial::BaudRate::BAUD_9600);
break;
case 19200:
serial_port.SetBaudRate(LibSerial::BaudRate::BAUD_19200);
break;
case 38400:
serial_port.SetBaudRate(LibSerial::BaudRate::BAUD_38400);
break;
case 57600:
serial_port.SetBaudRate(LibSerial::BaudRate::BAUD_57600);
break;
case 115200:
serial_port.SetBaudRate(LibSerial::BaudRate::BAUD_115200);
break;
default:
serial_port.SetBaudRate(LibSerial::BaudRate::BAUD_115200);
}
serial_port.SetCharacterSize(LibSerial::CharacterSize::CHAR_SIZE_8);
serial_port.SetFlowControl(LibSerial::FlowControl::FLOW_CONTROL_NONE);
serial_port.SetParity(LibSerial::Parity::PARITY_NONE);
serial_port.SetStopBits(LibSerial::StopBits::STOP_BITS_1);
} catch (const LibSerial::OpenFailed &) {
ROS_ERROR("The serial port did not open correctly.");
return;
}
const size_t ms_timeout = 150;
const char line_terminator = '\n';
while(ros::ok()){
std::string recvMsg;
try {
serial_port.ReadLine(recvMsg, line_terminator, ms_timeout);
} catch (const LibSerial::ReadTimeout &) {
std::cerr << "\nThe ReadLine() call has timed out." << std::endl;
}
recvMsg.erase(std::remove_if(recvMsg.begin(), recvMsg.end(), ::isspace), recvMsg.end());
if(recvMsg.empty()){
continue;
}
nmea_msgs::Sentence sentence;
sentence.header.frame_id = "rtk";
sentence.header.stamp = ros::Time().now();
sentence.sentence = recvMsg;
sentence_publisher.publish(sentence);
ros::spinOnce();
}
serial_port.Close();
return;
}
@@ -0,0 +1,9 @@
#include "bg620_serial_reader_core.h"
int main(int argc, char **argv){
setlocale(LC_CTYPE, "zh_CN.utf8");
ros::init(argc, argv, "rtk_bg620");
RtkSerialReaderBG620 rtkSerialReaderBG620;
rtkSerialReaderBG620.run();
return 0;
}