1.学习package
最近一直在思考,命令行上启动程序不太友好,想要GUI与用户交互,之前接触过Qt。自然,还是从Qt开始。找了一些地方,发现一个叫qt_ros的package,先用起来再慢慢做修改。
作者给的tutorial比较简单,catkin_make之后,我们有四个可执行的程序:他们分别是qadd_client、qadd_server、qlistener和qtalker。
前两个将ROS的service包含在了界面中,后面两个将ROS的node包含在GUI中了。下面我先启动后面两个看看:
这里,我使用的是系统的环境变量,做过ros by example的应该知道,在~/.bashrc如果想要连接到远程机器人的roscore是需要做一点改动的。在书上《Networking between a robot and a desktop computer》这一节。
因此,我这里的roscore就是在本机上启动的,这里勾选那个checkbox就好,点击connect(两边都要connect哦)。
查看node
非常简单,就是两个node和一个topic。根据tutorial,我们如果要建立自己的qt_ros的package,必须用catkin_ws/src下使用catkin_create_qt_package来建立,这里我建立的package目录名为qtros_btn.
2. 修改(举一反三)
既然是两个node和一个topic,我首先想到的是小乌龟,能否改掉它给乌龟发送信号,让它旋转呢?这里要有一点以前的知识,启动用rosrun turtlesim turtlesim_node,然后rostopic list一下,看到有关于cmd_vel的topic,然后给他发送信号,让乌龟一直旋转。
进入源程序,看到它的主要目录结构,这里我把自己用catkin_create_qt_package建立的和tutorial里放在一起作对比。
我们主要关注的还是qnode.cpp
这里我直接贴上程序,不难的,其实就是改掉它的publish那部分,因为我们是turtlesimnode作为subscriber,所以只要有publisher就好了
/**
* @file /src/qnode.cpp
*
* @brief Ros communication central!
*
* @date February 2011
**/
/*****************************************************************************
** Includes
*****************************************************************************/
#include <ros/ros.h>
#include <ros/network.h>
#include <string>
#include <std_msgs/String.h>
#include <geometry_msgs/Twist.h>
#include <sstream>
#include "../include/qtros_btn/qnode.hpp"
/*****************************************************************************
** Namespaces
*****************************************************************************/
namespace qtros_btn {
/*****************************************************************************
** Implementation
*****************************************************************************/
QNode::QNode(int argc, char** argv ) :
init_argc(argc),
init_argv(argv)
{}
QNode::~QNode() {
if(ros::isStarted()) {
ros::shutdown(); // explicitly needed since we use ros::start();
ros::waitForShutdown();
}
wait();
}
bool QNode::init() {
ros::init(init_argc,init_argv,"qtros_btn");
if ( ! ros::master::check() ) {
return false;
}
ros::start(); // explicitly needed since our nodehandle is going out of scope.
ros::NodeHandle n;
// Add your ros communications here.
chatter_publisher = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 1000);
start();
return true;
}
bool QNode::init(const std::string &master_url, const std::string &host_url) {
std::map<std::string,std::string> remappings;
remappings["__master"] = master_url;
remappings["__hostname"] = host_url;
ros::init(remappings,"qtros_btn");
if ( ! ros::master::check() ) {
return false;
}
ros::start(); // explicitly needed since our nodehandle is going out of scope.
ros::NodeHandle n;
// Add your ros communications here.
chatter_publisher = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 1000);
start();
return true;
}
void QNode::run() {
ros::Rate loop_rate(1);
int count = 0;
while ( ros::ok() ) {
geometry_msgs::Twist msg;
msg.linear.x = 2.0;
msg.angular.z = 2.0;
chatter_publisher.publish(msg);
// log(Info, msg.linear.x);
// log(Info, msg.angular.z);
logging_model.insertRows(logging_model.rowCount(),1);
std::stringstream logging_model_msg;
logging_model_msg << "[DEBUG] [" << ros::Time::now() << "]: " <<"linear = "<< msg.linear.x<<" angular = "<<msg.angular.z;
QVariant new_row(QString(logging_model_msg.str().c_str()));
logging_model.setData(logging_model.index(logging_model.rowCount()-1),new_row);
Q_EMIT loggingUpdated(); // used to readjust the scrollbar
ros::spinOnce();
loop_rate.sleep();
++count;
}
std::cout << "Ros shutdown, proceeding to close the gui." << std::endl;
Q_EMIT rosShutdown(); // used to signal the gui for a shutdown (useful to roslaunch)
}
void QNode::log(const LogLevel &level, const std::string &msg) {
logging_model.insertRows(logging_model.rowCount(),1);
std::stringstream logging_model_msg;
switch ( level ) {
case(Debug) : {
ROS_DEBUG_STREAM(msg);
logging_model_msg << "[DEBUG] [" << ros::Time::now() << "]: " << msg;
break;
}
case(Info) : {
ROS_INFO_STREAM(msg);
logging_model_msg << "[INFO] [" << ros::Time::now() << "]: " << msg;
break;
}
case(Warn) : {
ROS_WARN_STREAM(msg);
logging_model_msg << "[INFO] [" << ros::Time::now() << "]: " << msg;
break;
}
case(Error) : {
ROS_ERROR_STREAM(msg);
logging_model_msg << "[ERROR] [" << ros::Time::now() << "]: " << msg;
break;
}
case(Fatal) : {
ROS_FATAL_STREAM(msg);
logging_model_msg << "[FATAL] [" << ros::Time::now() << "]: " << msg;
break;
}
}
QVariant new_row(QString(logging_model_msg.str().c_str()));
logging_model.setData(logging_model.index(logging_model.rowCount()-1),new_row);
Q_EMIT loggingUpdated(); // used to readjust the scrollbar
}
} // namespace qtros_btn
说个题外话,这里我曾经尝试过删除一些ui文件,因为这里我主要只用到了一个button,但是发现,作者的控件都是关联的,不太容易修改,之前有改动过编译通过但是运行直接core dump的。它里面有多线程。
因此,我才会直接根据它的template修改,添加自己需要的部分。这是运行截图
3. 小结
有个地方我还需要说一下,在运行截图那里,实际的log界面,也就是打印信息的界面,作者的愿意图是封装了一个QNode::log()函数,它的第一个参数是level,第二个参数是string类型的msg,但是我怎么也没有找到将geometry_msg转化为string 的办法,没办法,直接霸王硬上弓,不通过那个函数直接调用里面的实现方法。
// log(Info, msg.linear.x);
// log(Info, msg.angular.z);
logging_model.insertRows(logging_model.rowCount(),1);
std::stringstream logging_model_msg;
logging_model_msg << "[DEBUG] [" << ros::Time::now() << "]: " <<"linear = "<< msg.linear.x<<" angular = "<<msg.angular.z;
QVariant new_row(QString(logging_model_msg.str().c_str()));
logging_model.setData(logging_model.index(logging_model.rowCount()-1),new_row);
Q_EMIT loggingUpdated(); // used to readjust the scrollbar
也就是这一段程序,试过下面这样用sprintf
std::stringstream s1;
std::stringstream s2;
sprintf(s1,"Sent: linear = %f", msg.linear.x);
sprintf(s2,"Sent: angular = %f", msg.angular.z);
log(Info,std::string(s1)+std::string (s2));
不过不行,大家也开动脑筋帮我思考一下吧。