之前有小伙伴问过LZ,如何移动仿真的小车呢?有一个方法是直接在终端中输入message,还有可以通过键盘控制小车.
1.创建控制包
在ROS中有两个工作空间方式,一个是使用catkin,另一个是rosbuild.
使用rosbuild将ROS程序安装到其他系统或架构上是很麻烦的,这是rosbuild存在的主要问题,也是后来为何要开发catkin的原因。
而为了解决上面这个问题,提高可移植性的关键是为ROS包提供安装目标,并服从FHS的安装布局。如果仅改变rosbuild并不是说不可能实现,但这实现似乎很难。
所以,catkin主要的设计过程是为CMake宏、变量和python写帮助函数catkin的设计者们也就此试图改进catkin的使用体验,以减少在程序构建时多依赖的项目对cmake和rosbuild的使用。 而解决这些问题的动机在于,rosbuild缓慢的构建速度是被人们抱怨最多的地方。
参考地址:https://blog.csdn.net/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_make2.编写源码
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^)/~