1. 程式人生 > >簡單的程式碼讓turtlebot動起來II(使用kobuki底座)走三角形

簡單的程式碼讓turtlebot動起來II(使用kobuki底座)走三角形

接著上一篇turtlebot的直行和旋轉。

ROS中,節點之間通過訊息溝通,主題相當於是對訊息進行路由和管理的匯流排。節點向主題釋出訊息即表示一個節點在傳送資料;節點訂閱某個主題即可以接受其他節點的訊息。在上一篇的直行和旋轉當中,我們只寫一個publisher,僅僅有話題的釋出並沒有話題的訂閱。於是就有了嘗試寫一個subsrciber的想法,實現turtlebot稍微複雜一點的運動。

想要讓turtlebot走一個三角形,那麼同上一篇,首先我們需要向底座釋出速度的資訊,publisher有了。那麼subscriber是什麼呢?三角形有自己的邊長,turtlebot要知道自己走了多遠,就需要知道自己的里程計資訊(轉載的一篇里程計相關的文章有介紹),即訂閱/odom主題。寫一個訂閱者的步驟:(1)初始化ROS系統(2)訂閱話題(3)Spin,等待訊息的到來(4)當一個訊息到達時,相應的Callback()函式被呼叫。

有了思路就可以實現了,附上程式碼:

 // 標頭檔案velcontrol.h  
  
#ifndef VELCONTROL_H  
#define VELCONTROL_H  
  
#include <ros/ros.h>  
#include <geometry_msgs/Twist.h>  
#include <geometry_msgs/TwistStamped.h>  
  
#include <nav_msgs/Odometry.h>  
#include "tf/LinearMath/Matrix3x3.h"  
#include "geometry_msgs/Quaternion.h"  
#define CV_PI   3.1415926535897932384626433832795  
  
#include <iostream>  
#include <stdlib.h>  
  
using namespace std ;  
  
typedef struct roPos  
{  
   double  x;  
   double  y;  
   double  theta;  
   void init(double x1, double x2, double x3)  
   {  
     x = x1;  
     y = x2;  
     theta = x3;  
   }  
} roPos;  
  
class velControl  
{  
public:  
    velControl();  
    roPos  start_pos_;  
    roPos  curr_pos_;  
  
  
protected:  
  
    void odomCallback(const nav_msgs::Odometry::ConstPtr& msg);  
  
private:  
    double rect_width_;  
    double rect_height_;  
    double offset_xy_;  
    double offset_theta_;  
  
    double vel_line_;  
    double vel_angle_;  
  
    int state_;  
    bool Is_Fininsh_;  
  
    ros::NodeHandle handle_;  
    ros::Publisher  vel_pub_ ;  
    ros::Subscriber sub_ ;  
    geometry_msgs::Twist  controlVel_;  
  
private:  
    double angleWrap(double angle);  
    bool init();  
    bool velocityControl();  
  
    int vel_pub_speed_;  
  
};  
  
#endif // VELCONTROL_H  
//執行檔案  velcontrol.cpp  
  
#include "velcontrol.h"  
velControl::velControl()  
{  
    init();  
    vel_pub_ = handle_.advertise<geometry_msgs::Twist>("/mobile_base/commands/velocity",1);  
    sub_ = handle_.subscribe("/odom", 1, &velControl::odomCallback,this); 
    //訂閱"/odom"話題,當一個新的訊息到達時,ROS將會呼叫velControl::odomCallback()函式。第二個引數是對列的長度,如果我們處理訊息的速度不夠快,會將收到的訊息緩衝下來,滿了的話,後面的到達的訊息將會覆蓋前面的訊息。
}
bool velControl::init()  
{  
    start_pos_.init(0.0, 0.0, 0.0);  
    curr_pos_.init(0.0, 0.0, 0.0);  
    rect_width_ = 0.5;  

    vel_line_ = 0.2;  
    vel_angle_ = 0.2;  
  
    vel_pub_speed_ = 5; 
  
    state_ = 0;  
    Is_Fininsh_ = true;  
}  
void velControl::odomCallback(const nav_msgs::Odometry::ConstPtr& msg)  
{  
    static int count = 0;  
    geometry_msgs::Quaternion orientation = msg->pose.pose.orientation;  
    tf::Matrix3x3 mat(tf::Quaternion(orientation.x, orientation.y, orientation.z, orientation.w));  
    double yaw, pitch, roll;  
    mat.getEulerYPR(yaw, pitch, roll);
    //Get the matrix represented as euler angles around YXZ, roundtrip with setEulerYPR. 
    //Yaw around Z axis; Pitch around Y axis; Roll around X axis
    curr_pos_.x = msg->pose.pose.position.x;  
    curr_pos_.y = msg->pose.pose.position.y;  
    curr_pos_.theta = yaw;  
    if(Is_Fininsh_)    
    {  
        start_pos_.x = curr_pos_.x;  
        start_pos_.y = curr_pos_.y;  
        start_pos_.theta = curr_pos_.theta;  
  
    }  
    count++;  
    if( count %= vel_pub_speed_ )  
    {  
        velocityControl();  
    }  
  
}  
bool velControl::velocityControl()  
{  
    geometry_msgs::Twist controlVel_;  
    switch (state_)  
    {  
    case 0: //走一個邊長,以下同理
        if ( abs(curr_pos_.x - start_pos_.x) < rect_width_ )  
        {  
            cout<<"0 curr_pos_x  "<<abs(curr_pos_.x - start_pos_.x) <<endl;  
            controlVel_.angular.z = 0.0;  
            controlVel_.linear.x  = vel_line_;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            Is_Fininsh_ = false;  
        }  
        else  
        {  
            cout<<"0 curr_pos_x  "<<abs(curr_pos_.x - start_pos_.x) <<endl;  
            controlVel_.angular.z = 0.0;  
            controlVel_.linear.x  = 0.0;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            state_ = 1;  
            Is_Fininsh_ = true;  
        }  
        break;  
    case 1: //旋轉120度,以下同理
        if ( abs(angleWrap(curr_pos_.theta - start_pos_.theta)) < CV_PI/3*2 )  
        {  
            cout<<"1 curr_pos_theta  "<<curr_pos_.theta - start_pos_.theta<<endl;  
            controlVel_.angular.z = vel_angle_;  
            controlVel_.linear.x  = 0;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            Is_Fininsh_ = false;  
        }  
        else  
        {  
            cout<<"1 curr_pos_theta  "<<curr_pos_.theta - start_pos_.theta<<endl;  
            controlVel_.angular.z = 0.0;  
            controlVel_.linear.x  = 0.0;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            state_ = 2;  
            Is_Fininsh_ = true;  
        }  
        break;  
    case 2: 
        if ( abs(curr_pos_.y - start_pos_.y ) < rect_width_)  
        {  
            cout<<"2 curr_pos_y  "<<abs(curr_pos_.y - start_pos_.y ) <<endl;  
            controlVel_.angular.z = 0.0;  
            controlVel_.linear.x  = vel_line_;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            Is_Fininsh_ = false;  
        }  
        else  
        {  
            cout<<"2 curr_pos_y  "<<abs(curr_pos_.y - start_pos_.y ) <<endl;  
            controlVel_.angular.z = 0.0;  
            controlVel_.linear.x  = 0.0;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            state_ = 3;  
            Is_Fininsh_ = true;  
        }  
        break;  
    case 3:   
        if ( abs(angleWrap(curr_pos_.theta - start_pos_.theta)) < CV_PI/3*2 )  
        {  
            cout<<"3 curr_pos_theta  "<< abs(angleWrap(curr_pos_.theta - start_pos_.theta)) <<endl;  
            controlVel_.angular.z = vel_angle_;  
            controlVel_.linear.x  = 0;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            Is_Fininsh_ = false;  
        }  
        else  
        {  
            cout<<"3 curr_pos_theta  "<<abs(angleWrap(curr_pos_.theta - start_pos_.theta)) <<endl;  
            controlVel_.angular.z = 0.0;  
            controlVel_.linear.x  = 0.0;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            state_ = 4;  
            Is_Fininsh_ = true;  
        }  
        break;  
    case 4: 
        if ( abs(curr_pos_.x - start_pos_.x) < rect_width_ )  
        {  
            cout<<"4 curr_pos_x  "<<abs(curr_pos_.x - start_pos_.x) <<endl;  
            controlVel_.angular.z = 0.0;  
            controlVel_.linear.x  = vel_line_;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            Is_Fininsh_ = false;  
        }  
        else  
        {  
            cout<<"4 curr_pos_x  "<<abs(curr_pos_.x - start_pos_.x) <<endl;  
            controlVel_.angular.z = 0.0;  
            controlVel_.linear.x  = 0.0;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            state_ = 5;  
            Is_Fininsh_ = true;  
        }  
        break;  
    case 5:   
        if ( abs(angleWrap(curr_pos_.theta - start_pos_.theta)) < CV_PI/3*2 )  
        {  
            cout<<"5 curr_pos_theta  "<<abs(angleWrap(curr_pos_.theta - start_pos_.theta)) <<endl;  
            controlVel_.angular.z = vel_angle_;  
            controlVel_.linear.x  = 0;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            Is_Fininsh_ = false;  
        }  
        else  
        {  
            cout<<"5 curr_pos_theta  "<<abs(angleWrap(curr_pos_.theta - start_pos_.theta)) <<endl;  
            controlVel_.angular.z = 0.0;  
            controlVel_.linear.x  = 0.0;  
            controlVel_.linear.y  = 0;  
            controlVel_.linear.z  = 0;  
            state_ = 6;  
            Is_Fininsh_ = true;  
        }  
        break;  
    default:  
        controlVel_.angular.z = 0.0;  
        controlVel_.linear.x  = 0.0;  
        controlVel_.linear.y  = 0;  
        controlVel_.linear.z  = 0;  
        Is_Fininsh_ = true;  
        break;  
    }  
    vel_pub_.publish(controlVel_);  
}  
double velControl::angleWrap(double angle)  
{  
    //把角度規劃到-pi至pi之間  
    if (angle>=CV_PI)  
        while (angle >= CV_PI)  
        { angle = angle-2*CV_PI;}  
    else if (angle < -1.0*CV_PI)  
        while (angle < -1.0*CV_PI)  
        { angle = angle+2*CV_PI;}  
  return angle;  
}  
  
  
int main(int argc,char** argv)  
{  
    ros::init(argc,argv,"odompub");  
    velControl odom_vel_;  
    ros::spin(); 
    //ros::spin()進入了一個迴圈,可以儘快的呼叫訊息的回撥函式。不要擔心,如果它沒有什麼事情可做時,它也不會浪費太多的CPU。當ros::ok()返回false時,ros::spin()將會退出。這就意味著,當ros::shutdown()被呼叫,或按下CTRL+C等情況,都可以退出。
    return 0;  
}  


ros::spin()和ros::spinonce()的區別在於:前者呼叫後不在返回,後者呼叫後繼續執行後面的程式。在使用ros::spin()的情況下,一般來說在初始化時已經設定好所有訊息的回撥,並且不需要其他背景程式執行。這樣以來,每次訊息到達時會執行使用者的回撥函式進行操作,相當於程式是訊息事件驅動的;而在使用ros::spinOnce()的情況下,一般來說僅僅使用回撥不足以完成任務,還需要其他輔助程式的執行:比如定時任務、資料處理、使用者介面等。

修改相應的CMakeList.txt,執行節點即可。但是觀察到turtlebot執行的軌跡並不是完美的正三角形,走起來的誤差還是存在的,誤差應該是和里程計有關。

最後感謝騰哥的指導!