视觉里程计的发布主函数


/**
 * @file ros_rgbd.cc
 * @author guoqing (1337841346@qq.com)
 * @brief ORB RGB-D 输入的ROS节点实现
 * @version 0.1
 * @date 2019-08-06
 *
 * @copyright Copyright (c) 2019
 *
 */

#include <iostream>
#include <algorithm>
#include <fstream>
#include <chrono>

#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <message_filters/subscriber.h>
#include <message_filters/time_synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>

#include <opencv2/core/core.hpp>

#include <System.h>
#include "Converter.h"

// geometry_msgs  环境配置  1: (1)包含头文件  #include "geometry_msgs/PoseStamped.h";
//         (2)find_package(  INCLUDE_DIRS  CATKIN_DEPENDS roscpp geometry_msgs  DEPENDS);
// 这样我们才可以使用这个类型的消息
// 2:  通过geometry_msgs::PoseStamped msg定义一个叫msg的对象,该对象可以使用上图中的header,pose两个数据成员.即你可以通过msg.header,msg.pose来调用数据类型为std_msgs/Header,geometry_msgs/Pose的两个数据成员.
#include "geometry_msgs/PoseStamped.h"
#include <tf/transform_broadcaster.h>
#include <nav_msgs/Odometry.h>

using namespace std;
using namespace cv;

cv::Mat CurrentTcw;
cv::Mat LastTcw;

class ImageGrabber
{
public:
    ImageGrabber(ORB_SLAM2::System *pSLAM) : mpSLAM(pSLAM) {}

    void GrabRGBD(const sensor_msgs::ImageConstPtr &msgRGB, const sensor_msgs::ImageConstPtr &msgD);

    ORB_SLAM2::System *mpSLAM;
};

int main(int argc, char **argv)
{
    ros::init(argc, argv, "RGBD");
    ros::start();

    if (argc != 3)
    {
        cerr << endl
             << "Usage: rosrun ORB_SLAM2 RGBD path_to_vocabulary path_to_settings" << endl;
        ros::shutdown();
        return 1;
    }

    // Create SLAM system. It initializes all system threads and gets ready to process frames.
    ORB_SLAM2::System SLAM(argv[1], argv[2], true);

    ImageGrabber igb(&SLAM);

    ros::NodeHandle nh;

    message_filters::Subscriber<sensor_msgs::Image> rgb_sub(nh, "/camera/color/image_raw", 1);
    message_filters::Subscriber<sensor_msgs::Image> depth_sub(nh, "/camera/aligned_depth_to_color/image_raw", 1);
    typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::Image, sensor_msgs::Image> sync_pol;
    message_filters::Synchronizer<sync_pol> sync(sync_pol(10), rgb_sub, depth_sub);
    sync.registerCallback(boost::bind(&ImageGrabber::GrabRGBD, &igb, _1, _2));

    //*********计算并且发布 Pub_Tcw*********
    // cout << "left and right 方向的平移  Twc.at<float>( 0,3 ) ==  " << mCurrentFrame.mTcw.at<float>(0, 3) << endl;
    //             cout << " up and down 方向的平移 Twc.at<float> ( 1,3 )==  " << mCurrentFrame.mTcw.at<float>(1, 3) << endl;
    //             cout << " ZZZZZZZZZZZZZZZZZ方向的平移 Twc.at<float> ( 2,3 ) ZZ==  " << mCurrentFrame.mTcw.at<float>(2, 3)*5 <<"米"<< endl;

    ///******************************************
    ros::Publisher odom_pub = nh.advertise<nav_msgs::Odometry>("odom", 50);
    tf::TransformBroadcaster odom_broadcaster;

    double scale = 5;                  // ORb中计算数值到米制单位的尺度
    LastTcw = CurrentTcw;              //计算vx,vy,v_yaw
    ros::Time current_time, last_time; //#转为s:ros::Time t_s = t.toSec();
    current_time = ros::Time::now();
    last_time = ros::Time::now();

    ros::Rate r(14.0); //用于固定频率循环的类,作用是使用1Hz的频率发布odom消息,当然,在实际系统中,往往需要更快的速度进行发布。
                       // Rate类对象的工作原理是,从上一次sleep结束后到下一次sleep开始时,如果经过的时间没有超过指定的频率时间,就通过sleep到指定频率时间。
                       // 比如指定Rate为10Hz(100ms),程序一次循环只用了10ms,那么sleep就会休眠90ms,然后进入下一次循环。
                       // 因此如果循环程序运行的时间大于指定Rate,那么这个Rate就失效了。
    const double vo_pose_covariance[36] = {1e-3, 0, 0, 0, 0, 0,
                                           0, 1e-3, 0, 0, 0, 0,
                                           0, 0, 1e6, 0, 0, 0,
                                           0, 0, 0, 1e6, 0, 0,
                                           0, 0, 0, 0, 1e6, 0,
                                           0, 0, 0, 0, 0, 1e3};
    const double vo_pose_covariance2[36] = {1e-9, 0, 0, 0, 0, 0,
                                            0, 1e-3, 1e-9, 0, 0, 0,
                                            0, 0, 1e6, 0, 0, 0,
                                            0, 0, 0, 1e6, 0, 0,
                                            0, 0, 0, 0, 1e6, 0,
                                            0, 0, 0, 0, 0, 1e-9};

    const double vo_twist_covariance[36] = {1e-3, 0, 0, 0, 0, 0,
                                            0, 1e-3, 0, 0, 0, 0,
                                            0, 0, 1e6, 0, 0, 0,
                                            0, 0, 0, 1e6, 0, 0,
                                            0, 0, 0, 0, 1e6, 0,
                                            0, 0, 0, 0, 0, 1e3};
    const double vo_twist_covariance2[36] = {1e-9, 0, 0, 0, 0, 0,
                                             0, 1e-3, 1e-9, 0, 0, 0,
                                             0, 0, 1e6, 0, 0, 0,
                                             0, 0, 0, 1e6, 0, 0,
                                             0, 0, 0, 0, 1e6, 0,
                                             0, 0, 0, 0, 0, 1e-9};
    // 我们设置机器人的默认前进速度,这三个参数在循环中更新发布出去
    double vx = 0.0;
    double vy = -0.0;
    double v_yaw = 0.0;
    double dx = 0.0;
    double dy = -0.0;
    double d_yaw = 0.0;
    double dt = 0.0;
    double current_yaw = 0.0;
    double last_yaw = 0.0;
    double current_x = 0.0;
    double last_x = 0.0;
    double current_y = 0.0;
    double last_y = 0.0;
    while (ros::ok()) //消息发布器在一个while里面一直循环发送
    {
        if (CurrentTcw.size().height > 0)
        {
            cv::Mat Pub_Rwc = CurrentTcw.rowRange(0, 3).colRange(0, 3).t();
            //以及平移向量
            cv::Mat Pub_twc = -Pub_Rwc * CurrentTcw.rowRange(0, 3).col(3);
            //用四元数表示旋转: vector<float> q = Converter::toQuaternion(Rwc);  //ORB中写成函数了,这里不用函数,更直白一些
            // Rotation_toMatrix3d
            Eigen::Matrix<double, 3, 3> eigMat;

            eigMat << Pub_Rwc.at<float>(0, 0), Pub_Rwc.at<float>(0, 1), Pub_Rwc.at<float>(0, 2),
                Pub_Rwc.at<float>(1, 0), Pub_Rwc.at<float>(1, 1), Pub_Rwc.at<float>(1, 2),
                Pub_Rwc.at<float>(2, 0), Pub_Rwc.at<float>(2, 1), Pub_Rwc.at<float>(2, 2);

            Eigen::Quaterniond q(eigMat);
            float q0 = q.w(); // ORB中的四元数计算结束
            float q1 = q.x();
            float q2 = q.y();
            float q3 = q.z();

            //欧拉角计算:原文链接:https://blog.csdn.net/xiaoma_bk/article/details/79082629
            double sinr_cosp = 2 * (q0 * q1 + q2 * q3);
            double cosr_cosp = 1 - 2 * (q0 * q0 + q1 * q1);

            float Roll_x_forward = atan2(sinr_cosp, cosr_cosp) * 57.3; // roll

            // pitch (y-axis rotation)
            double sinp = 2 * (q0 * q2 - q1 * q3);
            if (std::abs(sinp) >= 1)
                float Pitch_y_left = std::copysign(M_PI / 2, sinp) * 57.3; // use 90 degrees if out of range
            else
                float Pitch_y_left = std::asin(sinp) * 57.3;

            // yaw (z-axis rotation)
            double siny_cosp = 2 * (q1 * q2 + q0 * q3);
            double cosy_cosp = 1 - 2 * (q2 * q2 + q3 * q3);
            float Yaw_z_up = std::atan2(siny_cosp, cosy_cosp) * 57.3;
            current_yaw = (Yaw_z_up / 57.3); //弧度制
            current_time = ros::Time::now();
            current_x = CurrentTcw.at<float>(2, 3) * scale;
            last_x = LastTcw.at<float>(2, 3) * scale;

            current_y = CurrentTcw.at<float>(0, 3) * scale;
            last_y = LastTcw.at<float>(0, 3) * scale;
            cout << "main()   YYYYYYYYYYYYY,向左为正方向的平移  Twc.at<float>( 0,3 ) ==  " << CurrentTcw.at<float>(0, 3) * scale << "米" << endl;
            cout << " main() ZZZZZZZZZZZZZZZ,向上为正 方向,Z的平移 Twc.at<float> ( 1,3 )ZZ==  " << CurrentTcw.at<float>(1, 3) * scale << "米" << endl;
            cout << "main()  XXXXXXXXXXXXXXX,向后为正方向,X的平移 Twc.at<float> ( 2,3 ) XX==  " << current_x << "米" << endl;
            cout << "main()  偏航角(围绕Z_up旋转),yaw==  " << Yaw_z_up << "度" << endl;

            // 3.使用速度信息,来计算并更新里程计的信息,包括单位时间内机器人在x轴、y轴的坐标变化和角度的变化。在实际系统中,需要更具里程计的实际信息进行更新。
            //  nav_msgs  环境配置  1: (1)包含头文件  #include <tf/transform_broadcaster.h>;
            //                                                                                    #include <nav_msgs/Odometry.h>;
            //                                             2.            find_package(   nav_msgs  );

            //  参考教程:https://blog.csdn.net/datase/article/details/80535710    写nav_msgs的发布者
            //  1.  首先需要包含相关的头文件。   #include <tf/transform_broadcaster.h>
            // #include <nav_msgs/Odometry.h>
            // 2.定义一个消息发布者来发布“odom”消息,在定义一个tf广播,来发布tf变换信息。
            // since all odometry is 6DOF we'll need a quaternion created from yaw
            //为了兼容二维和三维的功能包,让消息结构更加通用,里程计的偏航角需要转换成四元数才能发布,使用ROS为我们提供了偏航角与四元数相互转换的功能。
            geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(current_yaw); //         //在odom参考系下角度是弧度制
            //创建一个tf发布需要使用的TransformStamped类型消息,然后根据消息结构填充当前的时间戳、参考系id、子参考系id,注意两个参考系的id必须要是“odom”和“base_link”。
            geometry_msgs::TransformStamped odom_trans;
            odom_trans.header.stamp = current_time;
            odom_trans.header.frame_id = "odom";
            odom_trans.child_frame_id = "camera";

            // NOTE: tf中的坐标系是右手坐标系,x_forward , y_left , z_up
            //在odom参考系下以x,y的单位都是m。
            odom_trans.transform.translation.x = current_x;
            odom_trans.transform.translation.y = current_y;
            odom_trans.transform.translation.z = 0.0;
            odom_trans.transform.rotation = odom_quat;

            // send the transform
            odom_broadcaster.sendTransform(odom_trans);

            //我们还要发布nav_msgs/Odometry消息,让导航包获取机器人的速度。创建消息变量,然后填充时间戳
            nav_msgs::Odometry odom;
            odom.header.stamp = current_time;
            odom.header.frame_id = "odom";
            //  填充机器人的位置、速度,然后发布消息。注意,我们发布的是机器人本体的信息,所以参考系需要填"base_link"。
            dt = (current_time - last_time).toSec();
            // cout << "Track()   YYYYYYYYYYYYY,向左为正方向的平移  Twc.at<float>( 0,3 ) ==  " << CurrentTcw.at<float>(0, 3) << endl;
            //     cout << " Track() ZZZZZZZZZZZZZZZ,向上为正 方向,Z的平移 Twc.at<float> ( 1,3 )ZZ==  " << CurrentTcw.at<float>(1, 3) << endl;
            //     cout << "Track()  XXXXXXXXXXXXXXX,向后为正方向,X的平移 Twc.at<float> ( 2,3 ) XX==  " << CurrentTcw.at<float>(2, 3)*5 <<"米"<< endl;

            dx = (CurrentTcw.at<float>(2, 3) - LastTcw.at<float>(2, 3)) * scale;
            dy = (CurrentTcw.at<float>(0, 3) - LastTcw.at<float>(0, 3)) * scale;
            d_yaw = current_yaw - last_yaw;

            vx = dx / dt;                          //     米/s
            vy = dy / dt;                          //     米/s
            v_yaw = d_yaw / dt;                    //     rad/s
            odom.pose.pose.position.x = current_x; //需要
            odom.pose.pose.position.y = current_y; //需要
            odom.pose.pose.position.z = 0.0;
            odom.pose.pose.orientation = odom_quat;

            odom.child_frame_id = "base_link";
            odom.twist.twist.linear.x = vx; //需要
            odom.twist.twist.linear.y = vy; //需要
            odom.twist.twist.linear.z = 0.0;
            odom.twist.twist.angular.z = v_yaw; //需要
            odom.twist.twist.angular.y = 0.0;
            odom.twist.twist.angular.x = 0.0;
            // //给里程计加上协方差 //参考:https://blog.csdn.net/buffei58/article/details/105617693
            if (vx != 0 && vy != 0 && v_yaw != 0) //如果小车velocity非零,考虑到运动中编码器可能带来的滑动误差,认为IMu的数据更可靠
            {

                memcpy(&odom.pose.covariance, vo_pose_covariance, sizeof(vo_pose_covariance)),
                    memcpy(&odom.twist.covariance, vo_twist_covariance, sizeof(vo_twist_covariance));
            }
            else 如果velocity是零,说明编码器的误差会比较小,认为编码器数据更可靠
            {
                memcpy(&odom.pose.covariance, vo_pose_covariance2, sizeof(vo_pose_covariance2)),
                    memcpy(&odom.twist.covariance, vo_twist_covariance2, sizeof(vo_twist_covariance2));
            }
            // publish the message
            odom_pub.publish(odom);
        } // if

        ros::spinOnce(); // 当spinOnce函数被调用时,spinOnce就会调用回调函数队列中第一个callback函数,此时callback函数被执行。
                         // spinOnce函数执行一次后,接着执行下面的语句。 等到下次spinOnce函数又被调用时,回调函数队列中第二个callback函数就会被调用,以此类推。

        last_time = current_time;
        LastTcw = CurrentTcw;
        last_yaw = current_yaw;

        typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::Image, sensor_msgs::Image> sync_pol; //时间戳相近
        message_filters::Synchronizer<sync_pol> sync(sync_pol(10), rgb_sub, depth_sub);
        sync.registerCallback(boost::bind(&ImageGrabber::GrabRGBD, &igb, _1, _2));

        r.sleep();
    }
    ///***********************

    // ros::spin();  //原来的,现在换成    while + ros::spinOnce()

    // Stop all threads
    SLAM.Shutdown();

    // Save camera trajectory
    SLAM.SaveKeyFrameTrajectoryTUM("KeyFrameTrajectory.txt");

    ros::shutdown();

    return 0;
}

void ImageGrabber::GrabRGBD(const sensor_msgs::ImageConstPtr &msgRGB, const sensor_msgs::ImageConstPtr &msgD)
{
    // Copy the ros image message to cv::Mat.
    cv_bridge::CvImageConstPtr cv_ptrRGB;
    try
    {
        cv_ptrRGB = cv_bridge::toCvShare(msgRGB);
    }
    catch (cv_bridge::Exception &e)
    {
        ROS_ERROR("cv_bridge exception: %s", e.what());
        return;
    }

    cv_bridge::CvImageConstPtr cv_ptrD;
    try
    {
        cv_ptrD = cv_bridge::toCvShare(msgD);
    }
    catch (cv_bridge::Exception &e)
    {
        ROS_ERROR("cv_bridge exception: %s", e.what());
        return;
    }

    // mpSLAM->TrackRGBD(cv_ptrRGB->image,cv_ptrD->image,cv_ptrRGB->header.stamp.toSec());
    // Pass the image to the SLAM system
    CurrentTcw = mpSLAM->Track(cv_ptrRGB->image, cv_ptrD->image, cv_ptrRGB->header.stamp.toSec());
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值