ROS(八):键盘控制smartcar

本文介绍如何在ROS环境中创建一个键盘控制的小车控制包,包括使用catkin工作空间创建控制包、编写源码实现键盘控制功能的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前有小伙伴问过LZ,如何移动仿真的小车呢?有一个方法是直接在终端中输入message,还有可以通过键盘控制小车.

1.创建控制包

在ROS中有两个工作空间方式,一个是使用catkin,另一个是rosbuild.

使用rosbuild将ROS程序安装到其他系统或架构上是很麻烦的,这是rosbuild存在的主要问题,也是后来为何要开发catkin的原因。

而为了解决上面这个问题,提高可移植性的关键是为ROS包提供安装目标,并服从FHS的安装布局。如果仅改变rosbuild并不是说不可能实现,但这实现似乎很难。
所以,catkin主要的设计过程是为CMake宏、变量和python写帮助函数catkin的设计者们也就此试图改进catkin的使用体验,以减少在程序构建时多依赖的项目对cmake和rosbuild的使用。 而解决这些问题的动机在于,rosbuild缓慢的构建速度是被人们抱怨最多的地方。

参考地址:https://blog.youkuaiyun.com/chishuideyu/article/details/53581013

LZ工作空间使用的是catkin,如果使用rosbuild方式可以参考https://www.ncnynl.com/archives/201609/846.html

首先,我们为键盘控制单独建立一个包:

cd ~/catkin_ws/src
catkin_create_pkg smartcar_teleop rospy geometry_msgs std_msgs roscpp  
cd ~/catkin_ws
catkin_make

2.编写源码
LZ也是新手,就加了点注释,键盘控制.

#include <termios.h>

#include <signal.h>

#include <math.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/poll.h>


#include <boost/thread/thread.hpp>
// ros/ros.h是一个实用的头文件,它引用了ROS系统中大部分常用的头文件
#include <ros/ros.h>

#include <geometry_msgs/Twist.h>


#define KEYCODE_W 0x77

#define KEYCODE_A 0x61

#define KEYCODE_S 0x73

#define KEYCODE_D 0x64


#define KEYCODE_A_CAP 0x41

#define KEYCODE_D_CAP 0x44

#define KEYCODE_S_CAP 0x53

#define KEYCODE_W_CAP 0x57


class SmartCarKeyboardTeleopNode {

private:

    double walk_vel_;

    double run_vel_;

    double yaw_rate_;

    double yaw_rate_run_;


    geometry_msgs::Twist cmdvel_;
    //为这个进程的节点创建一个句柄,第一个创建的NodeHandle会为节点进行初始化,
    //最后一个销毁的 NodeHandle 则会释放该节点所占用的所有资源
    ros::NodeHandle n_;

    //初始化发布节点
    ros::Publisher pub_;


public:

    SmartCarKeyboardTeleopNode() {
        //告诉master我们将要在cmd_vel(话题名)上发布geometry_msgs/Twist消息类型的消息
        //这样master就会告诉所有订阅了cmd_vel话题的节点,将要有数据发布.
        //第二个参数是发布序列的大小,如果我们发布消息的频率太高,
        //缓冲区中的消息在大于1000个的时候就会开始丢弃先前发布的消息
        //NodeHandle::advertise() 返回一个 ros::Publisher 对象,它有两个作用:
        //1) 它有一个 publish() 成员函数可以让你在topic上发布消息;
        //2) 如果消息类型不对,它会拒绝发布。

        pub_ = n_.advertise<geometry_msgs::Twist>("cmd_vel", 1);


        ros::NodeHandle n_private("~");

        n_private.param("walk_vel", walk_vel_, 0.5);

        n_private.param("run_vel", run_vel_, 1.0);

        n_private.param("yaw_rate", yaw_rate_, 1.0);

        n_private.param("yaw_rate_run", yaw_rate_run_, 1.5);

    }


    ~SmartCarKeyboardTeleopNode() {}

    void keyboardLoop();


    void stopRobot() {

        cmdvel_.linear.x = 0.0;

        cmdvel_.angular.z = 0.0;

        pub_.publish(cmdvel_);

    }

};


SmartCarKeyboardTeleopNode *tbk;

int kfd = 0;

struct termios cooked, raw;

bool done;


int main(int argc, char **argv) {
    //初始化ROS.它允许允许ROS通过命令进行名称重映射.
    //我们可以指定节点的名称,但是这里的名称必须是basename,名称内不能包含/等符号
    ros::init(argc, argv, "tbk", ros::init_options::AnonymousName | ros::init_options::NoSigintHandler);

    SmartCarKeyboardTeleopNode tbk;

    //重新定义一个线程
    boost::thread t = boost::thread(boost::bind(&SmartCarKeyboardTeleopNode::keyboardLoop, &tbk));


    //ros::spin()在调用后不会再返回,也就是你的主程序到这儿就不往下执行了,
    //而ros::spinOnce()后者在调用后还可以继续执行之后的程序。
    ros::spin();

    //中断线程
    t.interrupt();
    //由于线程中断,所以立即返回
    t.join();
    //速度置零
    tbk.stopRobot();

    tcsetattr(kfd, TCSANOW, &cooked);


    return (0);

}


void SmartCarKeyboardTeleopNode::keyboardLoop() {

    char c;

    double max_tv = walk_vel_;

    double max_rv = yaw_rate_;

    bool dirty = false;

    int speed = 0;

    int turn = 0;



    // get the console in raw mode  

    tcgetattr(kfd, &cooked);

    memcpy(&raw, &cooked, sizeof(struct termios));

    raw.c_lflag &= ~(ICANON | ECHO);

    raw.c_cc[VEOL] = 1;

    raw.c_cc[VEOF] = 2;

    tcsetattr(kfd, TCSANOW, &raw);


    puts("Reading from keyboard");

    puts("Use WASD keys to control the robot");

    puts("Press Shift to move faster");


    struct pollfd ufd;

    ufd.fd = kfd;

    ufd.events = POLLIN;


    for (;;) {

        boost::this_thread::interruption_point();



        // get the next event from the keyboard  

        int num;


        if ((num = poll(&ufd, 1, 250)) < 0) {

            perror("poll():");

            return;

        } else if (num > 0) {

            if (read(kfd, &c, 1) < 0) {

                perror("read():");

                return;

            }

        } else {

            if (dirty == true) {

                stopRobot();

                dirty = false;

            }


            continue;

        }


        switch (c) {

            case KEYCODE_W:

                max_tv = walk_vel_;

                speed = 1;

                turn = 0;

                dirty = true;

                break;

            case KEYCODE_S:

                max_tv = walk_vel_;

                speed = -1;

                turn = 0;

                dirty = true;

                break;

            case KEYCODE_A:

                max_rv = yaw_rate_;

                speed = 0;

                turn = 1;

                dirty = true;

                break;

            case KEYCODE_D:

                max_rv = yaw_rate_;

                speed = 0;

                turn = -1;

                dirty = true;

                break;


            case KEYCODE_W_CAP:

                max_tv = run_vel_;

                speed = 1;

                turn = 0;

                dirty = true;

                break;

            case KEYCODE_S_CAP:

                max_tv = run_vel_;

                speed = -1;

                turn = 0;

                dirty = true;

                break;

            case KEYCODE_A_CAP:

                max_rv = yaw_rate_run_;

                speed = 0;

                turn = 1;

                dirty = true;

                break;

            case KEYCODE_D_CAP:

                max_rv = yaw_rate_run_;

                speed = 0;

                turn = -1;

                dirty = true;

                break;

            default:

                max_tv = walk_vel_;

                max_rv = yaw_rate_;

                speed = 0;

                turn = 0;

                dirty = false;

        }

        cmdvel_.linear.x = speed * max_tv;

        cmdvel_.angular.z = turn * max_rv;

        pub_.publish(cmdvel_);

    }

}

在CMakeLists.txt中加上对应代码:

include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)

add_executable(smartcar_teleop src/teleop.cpp)
target_link_libraries(smartcar_teleop
  ${catkin_LIBRARIES}
  )

catkin_make之后,别忘了

source devel/setup.bash

按照ROS(六)先打开仿真小车

. devel/setup.bash
roslaunch smartcar_description smartcar_display.rviz.launch

这里写图片描述

这里写图片描述

在终端中就可以控制仿真的小车根据键盘的控制进行移动...

实验室就LZ一个人倒腾小车,伤不起呀\(^o^)/~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值