ROS基础内容
本文最后更新于37 天前,其中的信息可能已经过时,如有错误请发送邮件到suzhech@gmail.com

Ubuntu系统

要想学习ROS,要先安装Ubuntu系统,下面是Ubantu系统(Linux系统)的一些基本命令

# =============================================================================
# Linux 基础命令参考手册
# =============================================================================

# 1. 目录操作命令
# =============================================================================

# pwd = Print Work Directory (显示当前工作目录)
pwd

# cd = Change Directory (切换目录)
cd /path/to/directory    # 切换到指定目录
cd ..                    # 返回上一级目录
cd ~                     # 返回 home 目录
cd -                     # 返回上一次访问的目录

# ls = list (列出目录内容)
ls          # 列出当前目录内容
ls -a       # 显示所有文件(包括隐藏文件)
ls -l       # 详细列表显示(权限、大小、时间)
ls -la      # 显示所有文件的详细信息

# mkdir = make directory (创建目录)
mkdir dirname            # 创建目录
mkdir -p parent/child    # 创建多级目录

# 2. 文件操作命令
# =============================================================================

# rm = remove (删除文件或目录)
rm filename              # 删除文件
rm -r dirname            # 删除文件夹(递归删除)
rm -rf dirname           # 强制删除(不询问)

# mv = move (移动或重命名文件)
mv filename folder/filename    # 移动文件
mv oldname newname             # 重命名文件

# cp = copy (复制文件或目录)
cp file1 file2           # 复制文件
cp -r dir1 dir2          # 复制目录

# 3. 文件查看命令
# =============================================================================

# cat = catenate (连续输出文件内容)
cat filename             # 显示整个文件内容

# less = "比 more 更强" (分页查看文件)
less filename            # 分页查看文件内容

# tail = 取文件"尾巴" (查看文件末尾内容)
tail filename            # 查看文件末尾10行
tail -f filename         # 实时跟踪文件变化
tail -n 20 filename      # 查看文件末尾20行

# 4. 文本编辑器
# =============================================================================

# vi = visual interface (命令行文本编辑器)
# vim = vi improved (改进版 vi)
# gedit = GNOME 的图形化文本编辑器

# 编辑器使用示例:
gedit hello.cpp          # 打开图形化编辑器(推荐新手用)
vim hello.cpp            # 打开命令行编辑器
vi hello.cpp             # 打开 vi 编辑器

# =============================================================================
# 使用说明:
# 1. 在终端中直接输入命令即可执行
# 2. 注意命令和参数之间的空格
# 3. 谨慎使用 rm -rf 命令
# =============================================================================

ROS基础概念

ROS重要概念介绍

ROS基础工作空间(Workspace):存放工程开发相关文件的文件夹。类似一个IDE(例如Pycharm)新建一个工程,就是一个工作空间。包含4个文件夹:

src:代码空间(Source Space):放置功能包代码
build:编译空间(Build Space):编译过程中产生的中间文件,不用过多关注
devel:开发空间(Development Space):放置编译生成的可执行文件、库、脚本

install:安装空间(Install Space):存放可执行文件,与上面devel有一定重复

————————————————

先创建一个工作空间;

再创建一个功能包;catkin_create_pkg 包名 依赖包

编辑源文件;

编辑配置文件;

编译并执行。

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws
catkin_make
source devel/setup.bash
cd src
catkin_create_pkg robot_control roscpp std_msgs geometry_msgs

ROS代码编写

#include "ros/ros.h"   //写入ROS头文件
#include "geometry_msgs/Twist.h"  //写入速度消息处理头文件
//linear.x / linear.y / linear.z(线速度)
angular.x / angular.y / angular.z(角速度)

//arguement count/arguement vector 用于传参
int main(int argc,char** argv){

  ros::init(argc,argv,"move_robot_node");  //通过初始化一个节点,相当于在ROS Master系统中注册了一个节点用于通信和管理进程

  ros:Nodehandle nh; //定义一个节点句柄用于创建发布者和订阅者
  ros::Publisher pub=nh.advertise<geometry_msgs::Twist>("cmd_vel",10); //用Publisher创建了一个叫pub的发布者,发布的消息类型为Twist,发布到的话题名称为cmd_vel,队列长度为10

}

rate函数和sleep函数

ros::Rate define_rate_name(20) //会定义一个“节拍器”,以一秒二十次的频率工作

为了保证系统在运行过程中的稳定性,通常会使用sleep函数这个函数是为了保证ROS系统按照某一特定频率运行

特殊地,对于一个循环而言,例如:

for(int i=0;i<30;++i){
  some command;
  rate.sleep();
}

由于循环执行了三十次,我们又知道“节拍器”一秒钟运行三十次,也就相当于在这里我们执行了三秒,通过“节拍器”这一“睡眠特性”我们能够让整个ROS中所出现的代码以我们预想的秒数执行,达到稳定的状态。

根据sleep的这一特性,结合最终考核任务让小车前进一段距离后逆时针旋转60°后做矩形运动,我们可以编写一个前进函数和一个旋转函数,通过旋转函数来进行60°的逆时针运动和做矩形运动。具体示例:

void move_forward(double second){
    geometry_msgs::Twist t1;
    ros::Rate rate(10); //一秒十次,相当于0.1秒每次
    int n=second/0.1;
    t1.linear.x=1.0;
    for(int i=0;i<n;i++){
        pub.publish(t1);
        rate.sleep();
    }
    ROS_INFO("前进中")
}

但是在实际编译的过程中,会弹出编译错误的提示,这是因为我们没有对函数中pub定义造成的。因此正确的写法是:

void move_forward(ros::Publisher pub,double second){
    geometry_msgs::Twist t1;
    ros::Rate rate(10); //一秒十次,相当于0.1秒每次
    int n=second/0.1;
    t1.linear.x=1.0;
    for(int i=0;i<n;i++){
        pub.publish(t1);
        rate.sleep();
    }
    ROS_INFO("前进中")
}

前进函数的问题我们解决了,接下来我们开始思考如何处理旋转函数呢?任务中我们得知,要使小车旋转60度。

然而,在geometry包的Twist头文件中,只给了角速度的定义,似乎没有角度的定义,这就需要我们使用那个角度和弧度互换的公式,但是由此必定产生误差,这怎么办呢?在翻阅资料的过程中我得知,在cmath头文件中有一个数值M_PI定义了圆周率,那么由前进函数的定义,可以得到旋转函数的定义:

void turn_function(ros::Publisher pub_name,double angle){
    geometry_msgs::Twist t1;
    ros::Rate rate(10);
    t1.angular.z=0.1; //弧度制即0.1rad/s
    double angle_rad=angle*M_PI/180.0; //M_PI=3.1415926.... 这一步目的是将角度转化为弧度
    int n=angle_rad/0.1;
    for(int i=0;i<n;i++){
        pub_name.publish(t1);
        rate.sleep();
        ROS_INFO("旋转中");
    }
    t1.angular.z=0;
    pub_name.publish(t1);
    ROS_INFO("旋转完成")
}

上述代码完成之后,实际运行中却出现了在输出停止信号后机器人仍然在旋转,查阅CSDN及其他资料后发现,可能是停止指令未被接收到的原因,因此我们可以通过多次发送停止指令,来达到我们的目的。

void move_forward(ros::Publisher pub_name,double second){
    geometry_msgs::Twist t1;
    ros::Rate rate(10); //一秒十次,相当于0.1秒每次
    int n=second/0.1;
    t1.linear.x=0.1;
    for(int i=0;i<n;i++){
        pub_name.publish(t1);
        rate.sleep();
        ROS_INFO("前进中");
    }
//用pub_name作参数名的原因是和pub区分,实际查阅过程中发现可以用指针来代替,避免直接传参。
    for(int i=0;i<3;i++){
    t1.linear.x=0.0;
    pub_name.publish(t1);
    ROS_INFO("前进完成");}
}

void turn_function(ros::Publisher pub_name,double angle){
    geometry_msgs::Twist t1;
    ros::Rate rate(10);  //依然0.1秒每次
    t1.angular.z=0.1; //弧度制即0.1rad/s
    double angle_rad=angle*M_PI/180.0; //M_PI=3.1415926.... 这一步目的是将角度转化为弧度
    int n=angle_rad/0.01; //相当于rad / rad/s =s  然后再通过频率计算次数
    for(int i=0;i<n;i++){
        pub_name.publish(t1);
        rate.sleep();
        ROS_INFO("旋转中");
    }
    for(int i=0;i<3;i++){
    t1.angular.z=0;
    pub_name.publish(t1);
    ROS_INFO("旋转完成");}
}

在一次前进和一次旋转中,这段代码很出色地完成了任务,偏转角yaw=1.047近似等于60°,但在接下来做矩形的操作中,代码存在一些问题,于是又进行了一些改进:

#include "ros/ros.h"
#include "geometry_msgs/Twist.h"
#include <cmath>

void move_forward(ros::Publisher pub_name,double second){
    geometry_msgs::Twist t1;
    ros::Rate rate(10); //一秒十次,相当于0.1秒每次
    int n=second/0.1;
    t1.linear.x=0.1;
    for(int i=0;i<n;i++){
        pub_name.publish(t1);
        rate.sleep();
        ROS_INFO("前进中");
    }
    
    t1.linear.x=0.0;
    pub_name.publish(t1);
    ros::Duration(0.2).sleep();
    ROS_INFO("前进完成");
}

void turn_function(ros::Publisher pub_name,double angle){
    geometry_msgs::Twist t1;
    ros::Rate rate(10);  //依然0.1秒每次
    t1.angular.z=0.1; //弧度制即0.1rad/s
    double angle_rad=angle*M_PI/180.0; //M_PI=3.1415926.... 这一步目的是将角度转化为弧度
    int n=angle_rad/0.01; //相当于rad / rad/s =s  然后再通过频率计算次数
    for(int i=0;i<n;i++){
        pub_name.publish(t1);
        rate.sleep();
        ROS_INFO("旋转中");
    }

    t1.angular.z=0;
    pub_name.publish(t1);
      ros::Duration(0.2).sleep();
    ROS_INFO("旋转完成");
}

int main(int argc,char** argv){
    setlocale(LC_ALL, "");
    ros::init(argc,argv,"Robot_Control");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel",10);

    move_forward(pub,10);
    turn_function(pub,60);
    move_forward(pub,10);
    turn_function(pub,90);
    move_forward(pub,6);
    turn_function(pub,90);
    move_forward(pub,10);
    turn_function(pub,90);
    move_forward(pub,6);
    return 0;
}

在实际运行中,很快就发现小车的运行与预期存在差距,继续学习后发现这种方式造成的偏差巨大,于是有了更加精确的代码:

#include "ros/ros.h"
#include "nav_msgs/Odometry.h"
#include "tf/transform_datatypes.h"
#include <cmath>

void callback(const nav_msgs::Odometry::ConstPtr& msg){
    
    tf::Quaternion test(
        msg -> pose.pose.orientation.x,
        msg -> pose.pose.orientation.y,
        msg -> pose.pose.orientation.z,
        msg -> pose.pose.orientation.w
    );

    tf::Matrix3x3 m(test);
    double roll,pitch,yaw;
    m.getRPY(roll,pitch,yaw);
    ROS_INFO("当前机器人偏转弧度角:%.2f,偏转角度%.2f; 位置坐标:x:%.2f,y:%2.f,z:%.2f",yaw,yaw*180/M_PI,msg->pose.pose.position.x,msg->pose.pose.position.y,msg->pose.pose.position.z );
}

int main(int argc,char** argv){
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"postion_robot");
    ros::NodeHandle nh;
    ros::Subscriber sub = nh.subscribe("odom",10,callback);
    ros::spin();

    return 0;
}

使用这段代码能够很好的获取机器人的偏转角,这是利用了tf坐标工具中四元数转偏转角的方法,通过这种实时获取偏转角的方法我们可以即时获得机器人的偏转角,以此达到更加稳定的控制,因此我们有了以下的代码:

#include "ros/ros.h"
#include "geometry_msgs/Twist.h"
#include "tf/tf.h"
#include "nav_msgs/Odometry.h"
#include <cmath>

double yaw=0;
void callback(const nav_msgs::Odometry::ConstPtr& msg){
    
    tf::Quaternion test(
        msg -> pose.pose.orientation.x,
        msg -> pose.pose.orientation.y,
        msg -> pose.pose.orientation.z,
        msg -> pose.pose.orientation.w
    );

    tf::Matrix3x3 m(test);
    double roll,pitch;
    m.getRPY(roll,pitch,yaw);
}

void move_forward(ros::Publisher pub_name,double second){
    geometry_msgs::Twist t1;
    ros::Rate rate(10); //一秒十次,相当于0.1秒每次
    int n=second/0.1;
    t1.linear.x=0.1;
    for(int i=0;i<n;i++){
        pub_name.publish(t1);
        rate.sleep();
        ROS_INFO("前进中");
    }
    
    t1.linear.x=0.0;
    pub_name.publish(t1);
    ros::Duration(0.2).sleep();
    ROS_INFO("前进完成");
}

void turn_function(ros::Publisher pub_name,double target_angle){
    geometry_msgs::Twist t1;
    ros::Rate rate(10);  //依然0.1秒每次
    t1.angular.z=0.3; //弧度制即0.3rad/s
    double start_rad=yaw; //只会赋值一次
    double target_rad=target_angle*M_PI/180.0;

    while(ros::ok()){
        double delta = yaw - start_rad;
        if (delta > M_PI) delta -= 2 * M_PI;
        if (delta < -M_PI) delta += 2 * M_PI;//这里是为了旋转最短方案,也是为了偏转角不越界
        if(fabs(delta) >= target_rad){
            break;
        }
        else {pub_name.publish(t1);
            ros::spinOnce();
            rate.sleep();
        }

    }

    t1.angular.z=0;
    pub_name.publish(t1);
    ros::Duration(0.2).sleep();
    ROS_INFO("旋转完成");
}

int main(int argc,char** argv){
    setlocale(LC_ALL, "");
    ros::init(argc,argv,"Robot_Control");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel",10);
    ros::Subscriber sub = nh.subscribe("/odom", 10, callback);
    
    move_forward(pub,10);
    turn_function(pub,60);
    move_forward(pub,10);
    turn_function(pub,90);//
    move_forward(pub,6);
    turn_function(pub,90);
    move_forward(pub,10);
    turn_function(pub,90);
    move_forward(pub,6);
    return 0;
}

实际运行时发现,转第三个角度时,运行与预期存在差距:

观察后发现,这可能是由于小车速度过快导致命令接收不到导致的,于是在旋转函数开头中加入等待周期,以使其准确获得信息,如此,得到以下最终代码:

#include "ros/ros.h"
#include "geometry_msgs/Twist.h"
#include "tf/tf.h"
#include "nav_msgs/Odometry.h"
#include <cmath>

double yaw=0;
void callback(const nav_msgs::Odometry::ConstPtr& msg){
    
    tf::Quaternion test(
        msg -> pose.pose.orientation.x,
        msg -> pose.pose.orientation.y,
        msg -> pose.pose.orientation.z,
        msg -> pose.pose.orientation.w
    );

    tf::Matrix3x3 m(test);
    double roll,pitch;
    m.getRPY(roll,pitch,yaw);
}

void move_forward(ros::Publisher pub_name,double second){
    geometry_msgs::Twist t1;
    ros::Rate rate(10); //一秒十次,相当于0.1秒每次
    int n=second/0.1;
    t1.linear.x=0.1;
    for(int i=0;i<n;i++){
        pub_name.publish(t1);
        rate.sleep();
        ROS_INFO("前进中");
    }
    
    t1.linear.x=0.0;
    pub_name.publish(t1);
    ros::Duration(0.2).sleep();
    ROS_INFO("前进完成");
}

void turn_function(ros::Publisher pub_name,double target_angle){
    geometry_msgs::Twist t1;
    ros::Rate rate(10);  //依然0.1秒每次
    t1.angular.z=0.3; //弧度制即0.3rad/s
    ros::spinOnce();
    ros::Duration(0.1).sleep();
    double start_rad=yaw; //只会赋值一次
    double target_rad=target_angle*M_PI/180.0;

    while(ros::ok()){
        double delta = yaw - start_rad;
        if (delta > M_PI) delta -= 2 * M_PI;
        if (delta < -M_PI) delta += 2 * M_PI;
        if(fabs(delta) >= target_rad){
            break;
        }
        else {pub_name.publish(t1);
            ros::spinOnce();
            rate.sleep();
        }

    }

    t1.angular.z=0;
    pub_name.publish(t1);
    ros::Duration(0.2).sleep();
    ROS_INFO("旋转完成");
}

int main(int argc,char** argv){
    setlocale(LC_ALL, "");
    ros::init(argc,argv,"Robot_Control");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel",10);
    ros::Subscriber sub = nh.subscribe("/odom", 10, callback);
    
    move_forward(pub,10);
    turn_function(pub,60);
    move_forward(pub,10);
    turn_function(pub,90);
    move_forward(pub,6);
    turn_function(pub,90);
    move_forward(pub,10);
    turn_function(pub,90);
    move_forward(pub,6);
    return 0;
}

在提交作业录制视频的过程中,几次测试发现小车不一定能走出完美矩形,说明代码存在问题,由于加分项目部分引入了move_base服务器,move_base能够发布详细旋转(四元数)于是决定用该方法继续完善代码,于是得到最终代码:

#include "ros/ros.h"
#include "geometry_msgs/Twist.h"
#include "tf/tf.h"
#include "nav_msgs/Odometry.h"
#include <cmath>
#include <move_base_msgs/MoveBaseAction.h>
#include <actionlib/client/simple_action_client.h>


double yaw=0,current_x=0,current_y=0;
typedef actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> MoveBaseClient;
void callback(const nav_msgs::Odometry::ConstPtr& msg){
    current_x = msg->pose.pose.position.x;
    current_y = msg->pose.pose.position.y;
    tf::Quaternion q(
        msg -> pose.pose.orientation.x,
        msg -> pose.pose.orientation.y,
        msg -> pose.pose.orientation.z,
        msg -> pose.pose.orientation.w
    );

    tf::Matrix3x3 m(q);
    double roll,pitch;
    m.getRPY(roll,pitch,yaw);
}

void move_forward(ros::Publisher pub_name,double second){
    geometry_msgs::Twist t1;
    ros::Rate rate(10); //一秒十次,相当于0.1秒每次
    int n=second/0.1;
    t1.linear.x=0.1;
    for(int i=0;i<n;i++){
        pub_name.publish(t1);
        rate.sleep();
        ROS_INFO("前进中");
    }
    
    t1.linear.x=0.0;
    pub_name.publish(t1);
    ros::Duration(0.2).sleep();
    ROS_INFO("前进完成");
}

void turn_function(MoveBaseClient& client,double target_angle){
    ros::Duration(2).sleep();
    ros::spinOnce();
    double target_yaw =yaw+(target_angle*M_PI/180.0);
    while(target_yaw> M_PI) target_yaw -= 2*M_PI;
    while(target_yaw<-M_PI) target_yaw += 2*M_PI;

   
    move_base_msgs::MoveBaseGoal goal;
    goal.target_pose.header.frame_id = "map";
    goal.target_pose.header.stamp = ros::Time::now();

    goal.target_pose.pose.position.x= current_x;
    goal.target_pose.pose.position.y= current_y;
    goal.target_pose.pose.position.z= 0;
    goal.target_pose.pose.orientation = tf::createQuaternionMsgFromYaw(target_yaw);
    client.sendGoal(goal);
    ros::spinOnce();
    ros::Duration(2.0).sleep();
    ROS_INFO("旋转完成");
    yaw = target_yaw;
}

int main(int argc,char** argv){
    setlocale(LC_ALL, "");
    ros::init(argc,argv,"Robot_Control");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel",10);
    ros::Subscriber sub = nh.subscribe("/odom", 10, callback);
    
    ros::Duration(1.0).sleep();
    ros::spinOnce();
    
    MoveBaseClient client("move_base",true);
    client.waitForServer(ros::Duration(5.0));
    
    move_forward(pub,10);
    turn_function(client,60);
    move_forward(pub,10);
    turn_function(client,90);
    move_forward(pub,6);
    turn_function(client,90);
    move_forward(pub,10);
    turn_function(client,90);
    move_forward(pub,6);
    return 0;
}

真的是这样吗?然而,在实际运动中出现了以下问题:

当我测试了一天一夜之后,终于发现了错误,速度太快了,小车反应不过来!等待时间要久一点,于是我终于终于完成了最终代码:

#include "ros/ros.h"
#include "geometry_msgs/Twist.h"
#include "tf/tf.h"
#include "nav_msgs/Odometry.h"
#include <cmath>

double yaw=0;
void callback(const nav_msgs::Odometry::ConstPtr& msg){
    
    tf::Quaternion test(
        msg -> pose.pose.orientation.x,
        msg -> pose.pose.orientation.y,
        msg -> pose.pose.orientation.z,
        msg -> pose.pose.orientation.w
    );

    tf::Matrix3x3 m(test);
    double roll,pitch;
    m.getRPY(roll,pitch,yaw);
}

void move_forward(ros::Publisher pub_name,double second){
    geometry_msgs::Twist t1;
    ros::Rate rate(10); //一秒十次,相当于0.1秒每次
    int n=second/0.1;
    t1.linear.x=0.1;
    for(int i=0;i<n;i++){
        pub_name.publish(t1);
        rate.sleep();
        ROS_INFO("前进中");
    }
    
    t1.linear.x=0.0;
    pub_name.publish(t1);
    ros::Duration(0.2).sleep();
    ROS_INFO("前进完成");
}

void turn_function(ros::Publisher pub_name,double target_angle){
    geometry_msgs::Twist t1;
    ros::Rate rate(10);  //依然0.1秒每次
    t1.angular.z=0.3; //弧度制即0.3rad/s
    ros::spinOnce();
    ros::Duration(0.1).sleep();
    double start_rad=yaw; //只会赋值一次
    double target_rad=target_angle*M_PI/180.0;

    while(ros::ok()){
        double delta = yaw - start_rad;
        if (delta > M_PI) delta -= 2 * M_PI;
        if (delta < -M_PI) delta += 2 * M_PI;
        if(fabs(delta) >= target_rad){
            break;
        }
        else {pub_name.publish(t1);
            ros::spinOnce();
            rate.sleep();
        }

    }

    t1.angular.z=0;
    pub_name.publish(t1);
    ros::Duration(0.2).sleep();
    ROS_INFO("旋转完成");
}

int main(int argc,char** argv){
    setlocale(LC_ALL, "");
    ros::init(argc,argv,"Robot_Control");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel",10);
    ros::Subscriber sub = nh.subscribe("/odom", 10, callback);
    
    move_forward(pub,10);
    turn_function(pub,60);
    move_forward(pub,10);
    turn_function(pub,90);
    move_forward(pub,6);
    turn_function(pub,90);
    move_forward(pub,10);
    turn_function(pub,90);
    move_forward(pub,6);
    return 0;
}

可惜事情的发展并没有想象中的顺利,在我想要提交作业录制视频的过程中,发现几次测试小车行走路线都不一致,于是经过几天的对代码的精进和调试,终于得到以下代码:

#include "ros/ros.h"
#include "geometry_msgs/Twist.h"
#include "tf/tf.h"
#include "nav_msgs/Odometry.h"
#include <cmath>
#include <move_base_msgs/MoveBaseAction.h>
#include <actionlib/client/simple_action_client.h>

double yaw=0,current_x=0,current_y=0;
typedef actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> MoveBaseClient;

void callback(const nav_msgs::Odometry::ConstPtr& msg){
    current_x = msg->pose.pose.position.x;
    current_y = msg->pose.pose.position.y;
    tf::Quaternion q(
        msg -> pose.pose.orientation.x,
        msg -> pose.pose.orientation.y,
        msg -> pose.pose.orientation.z,
        msg -> pose.pose.orientation.w
    );

    tf::Matrix3x3 m(q);
    double roll,pitch;
    m.getRPY(roll,pitch,yaw);
}


void move_forward(ros::Publisher pub_name,double second){
    geometry_msgs::Twist t1;
    ros::Rate rate(10); 
    int n=second/0.1;
    t1.linear.x=0.1;
    t1.angular.z=0.0;
    for(int i=0;i<n;i++){
        pub_name.publish(t1);
        rate.sleep();
        ROS_INFO("前进中");
        ros::spinOnce();  
    }
    
    t1.linear.x=0.0;
    pub_name.publish(t1);
    ROS_INFO("前进完毕");
    ros::Duration(0.5).sleep();
}

void turn_function(ros::Publisher& pub_name,double target_angle){
    ros::spinOnce(); 
    ros::Rate rate(50);
    double start_yaw = yaw;
    double target_yaw = start_yaw + target_angle * M_PI/180.0;
    while(target_yaw > M_PI) target_yaw -= 2*M_PI;
    while(target_yaw < -M_PI) target_yaw += 2*M_PI;

    geometry_msgs::Twist twist;
    double angular_speed = 0.3; 
    double delta = target_yaw - yaw;

    while(ros::ok() && fabs(delta) > 0.02){  
        ros::spinOnce();
        delta = target_yaw - yaw;
        if (delta > M_PI) delta -= 2*M_PI;
        if (delta < -M_PI) delta += 2*M_PI;

        twist.linear.x = 0.0;
        twist.angular.z = (delta > 0) ? angular_speed : -angular_speed;
        pub_name.publish(twist);
        rate.sleep();
    }

    twist.angular.z = 0.0;
    pub_name.publish(twist);
    ROS_INFO("旋转完成");
    ros::Duration(0.5).sleep();
}

int main(int argc,char** argv){
    setlocale(LC_ALL, "");
    ros::init(argc,argv,"Robot_Control");
    ros::NodeHandle nh;
    ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel",10);
    ros::Subscriber sub = nh.subscribe("/odom", 10, callback);
    ros::Duration(2.0).sleep();
    ros::spinOnce();
    
    move_forward(pub,10);
    turn_function(pub,60);
    move_forward(pub,10);
    turn_function(pub,90);
    move_forward(pub,6);
    turn_function(pub,90);
    move_forward(pub,10);
    turn_function(pub,90);
    move_forward(pub,6);

    return 0;
}

环境变量问题

roslaunch turtlebot3_gazebo turtlebot3_world.launch

echo “export TURTLEBOT3_MODEL=burger” >> ~/.bashrc
source ~/.bashrc

burger(最小型)

waffle(带摄像头、激光雷达)

waffle_pi(带树莓派摄像头)

roslaunch turtlebot3_gazebo turtlebot3_gazebo_rviz.launch

1.rosrun robot_control move_robot

2.rosrun robot_control path

3.roslaunch turtlebot3_gazebo turtlebot3_empty_world.launch

4.roslaunch turtlebot3_gazebo turtlebot3_gazebo_rviz.launch map=odom

进阶任务

#include <ros/ros.h>
#include <move_base_msgs/MoveBaseAction.h>
#include <actionlib/client/simple_action_client.h>
#include <tf/tf.h>
#include <cmath>

int main(int argc,char** argv){
    ros::init(argc,argv,"nav_goal");
    actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> MoveBaseClient;  //定义一个叫MoveBaseClient的新类型

    MoveBaseClient client("move_base",true); //用这个新类型创建一个叫client的变量,move_base是动作服务器名称,true是自动处理消息
    ROS_INFO("等待启动成功");
    client.waitForServer(ros::Duration(5.0));
    ROS_INFO("连接成功!");

    move_base_msgs::MoveBaseGoal goal;
    
    goal.target_pose.header.frame_id = "map";  // 使用全局地图
    goal.target_pose.header.stamp = ros::Time::now();    //时间戳获取当前时间

    goal.target_pose.pose.position.x= 3.0;
    goal.target_pose.pose.position.y= 2.0;
    
    double yaw=M_PI;
    tf::Quaternion q = tf::createQuaternionFromYaw(yaw);
    goal.target_pose.pose.orientation.x = q.x();
    goal.target_pose.pose.orientation.y = q.y();
    goal.target_pose.pose.orientation.z = q.z();
    goal.target_pose.pose.orientation.w = q.w();
    /*这里和旋转部分反过来,偏转角转化为四元数是因为在旋转中偏转角更直观,
    而实际上四元数能避免万向锁等问题,系统误差更小。*/
    /*
    goal (MoveBaseGoal)
    └── target_pose (PoseStamped)
    ├── header (Header)
    │   ├── seq
    │   ├── stamp
    │   └── frame_id
    └── pose (Pose)
        ├── position (Point)
        │   ├── x  ← 我们设置的X坐标
        │   ├── y  ← Y坐标
        │   └── z  ← Z坐标
        └── orientation (Quaternion)
            ├── x
            ├── y
            ├── z
            └── w
    */
    

    ROS_INFO("发送导航目标...");
    client.sendGoal(goal);

    client.waitForResult();//等一下结果,避免没收到

    if(client.getState() == actionlib::SimpleClientGoalState::SUCCEEDED)
        ROS_INFO("到达目标点!");
    else
        ROS_WARN("未能到达目标点!");

    return 0;
}

通过这样我们可以让机器人移动到指定的点x=3.0,y=2.0。于是我们创建一个数组就能满足多点导航:

#include <ros/ros.h>
#include <move_base_msgs/MoveBaseAction.h>
#include <actionlib/client/simple_action_client.h>
#include <tf/tf.h>
#include <cmath>

int main(int argc,char** argv){
    setlocale(LC_ALL, "");
    ros::init(argc,argv,"nav_goal");
    typedef actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> MoveBaseClient;  //定义一个叫MoveBaseClient的新类型

    MoveBaseClient client("move_base",true); //用这个新类型创建一个叫client的变量,move_base是动作服务器名称,true是自动处理消息
    ROS_INFO("等待启动成功");
    client.waitForServer(ros::Duration(5.0));
    ROS_INFO("连接成功!");

    double goals[][3]={
        {-1.5,0.5,0},
        {2.05,0.3,0},
        {3,2,M_PI},
        {1.06,2.48,0},
        {-6.04,5.04,0},
        {-6.5,-3.42,0},
    };
    
    for(int i=0;i<6;++i){
    move_base_msgs::MoveBaseGoal goal;
    goal.target_pose.header.frame_id = "map";  // 使用全局地图
    goal.target_pose.header.stamp = ros::Time::now();    //时间戳获取当前时间

    goal.target_pose.pose.position.x=goals[i][0] ;
    goal.target_pose.pose.position.y=goals[i][1] ;
    
    double yaw=goals[i][2];
    tf::Quaternion q = tf::createQuaternionFromYaw(yaw);
    goal.target_pose.pose.orientation.x = q.x();
    goal.target_pose.pose.orientation.y = q.y();
    goal.target_pose.pose.orientation.z = q.z();
    goal.target_pose.pose.orientation.w = q.w();
    /*这里和旋转部分反过来,偏转角转化为四元数是因为在旋转中偏转角更直观,
    而实际上四元数能避免万向锁等问题,系统误差更小。*/
    /*
    goal (MoveBaseGoal)
    └── target_pose (PoseStamped)
    ├── header (Header)
    │   ├── seq
    │   ├── stamp
    │   └── frame_id
    └── pose (Pose)
        ├── position (Point)
        │   ├── x  ← 我们设置的X坐标
        │   ├── y  ← Y坐标
        │   └── z  ← Z坐标
        └── orientation (Quaternion)
            ├── x
            ├── y
            ├── z
            └── w
    */
    

    ROS_INFO("发送导航目标...");
    client.sendGoal(goal);

    client.waitForResult();//等一下结果,避免没收到

    if(client.getState() == actionlib::SimpleClientGoalState::SUCCEEDED)
        ROS_INFO("到达目标点!");
    else
        ROS_WARN("未能到达目标点!");
    
    ros::Duration(1.0).sleep();
    }

    ROS_INFO("至此,终于完成了所有考核任务");

    return 0;
}

  • roslaunch turtlebot3_gazebo turtlebot3_house.launch
  • roslaunch turtlebot3_navigation turtlebot3_navigation.launch map_file:=/home/su/map.yaml
  • rosrun robot_navigation nav_node

ROS基础内容 by Suzhech

评论

  1. Suzhech的头像
    博主
    Windows Edge
    河北省秦皇岛市 电信
    1 月前
    2025-10-22 21:47:16

    lz好棒,加油输出更多优质内容!

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇