PS:采用的视频教程是大学慕课的《机器人操作系统入门》 https://www.icourse163.org/learn/ISCAS-1002580008#/learn/announce, 由于使用的是Ubuntu 18.04,使用的ROS版本为 Melodic 和视频所用的ROS kinetic版本有所出入,产生以下问题:1.使用模块不在,解决方法 用 git clone 克隆;2.加载robot_sim_demo只有地板,没有模型和小车,解决办法 由于gazebo9版本不兼容这个demo的模型,所以要么换低版本gazebo,要么换模型。
ROS中相关函数的使用方法可以见网站:https://docs.ros.org/api/roscpp/html/
以下记录下roscpp中相关的程序,以便自查自用。所有代码均可以从github找到。
https://github.com/DroidAITech/ROS-Academy-for-Beginners
cd ~/catkin_ws/src
catkin_crate_pkg topic_demo roscpp rospy std_msgs
个人笔记:其中catkin_ws是工作空间;第二行指令是创建一个包,后面是依赖包
cd topic_demo
mkdir msg
cd msg
vim gps.msg
gps.msg的内容如下:
float32 x
float32 y
string state
个人笔记:msg文件不能直接使用,要经过catkin_make编译之后,会在 ~/catkin_ws/devel/include/topic_demo
中生成一个 gps.h
文件,然后在c++中include这个头文件就可以使用了,使用方法为:topic_demo::gps msg;
//ROS头文件
#include
//自定义msg产生的头文件
#include
int main(int argc, char **argv)
{
//用于解析ROS参数,第三个参数为本节点名
ros::init(argc, argv, "talker");
//实例化句柄,初始化node
ros::NodeHandle nh;
//创建publisher
ros::Publisher pub = nh.advertise("gps_info", 1);
//自定义gps msg
topic_demo::gps msg;
msg.x = 1.0;
msg.y = 1.0;
msg.state = "working";
//定义发布的频率
ros::Rate loop_rate(1.0);
//循环发布msg
while (ros::ok())
{
//以指数增长,每隔1秒更新一次
msg.x = 1.03 * msg.x ;
msg.y = 1.01 * msg.y;
ROS_INFO("Talker: GPS: x = %f, y = %f ", msg.x ,msg.y);
//以1Hz的频率发布msg
pub.publish(msg);
//根据前面定义的频率, sleep 1s
loop_rate.sleep();//根据前面的定义的loop_rate,设置1s的暂停
}
return 0;
}
个人笔记:1.ros::Publisher pub = nh.advertise
中第一个参数为发布topic的名称 第二个参数为储存数据的队列长度,队列长度越短,处理的速度越快:2.由于消息是不停发送的,因而需要加一个while循环
//ROS头文件
#include
//包含自定义msg产生的头文件
#include
//ROS标准msg头文件
#include
void gpsCallback(const topic_demo::gps::ConstPtr &msg)
{
//计算离原点(0,0)的距离
std_msgs::Float32 distance;
distance.data = sqrt(pow(msg->x,2)+pow(msg->y,2));
//float distance = sqrt(pow(msg->x,2)+pow(msg->y,2));
ROS_INFO("Listener: Distance to origin = %f, state: %s",distance.data,msg->state.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("gps_info", 1, gpsCallback);
//ros::spin()用于调用所有可触发的回调函数。将进入循环,不会返回,类似于在循环里反复调用ros::spinOnce()。
ros::spin();
return 0;
}
个人笔记: 1.ros::Subscriber sub = n.subscribe("gps_info", 1, gpsCallback);
中第一个参数是监听的哪个topic,第二个参数是subscriber的队列,第三个参数是回调函数,回调函数是一个指针,用来处理信息的函数。2.void gpsCallback(const topic_demo::gps::ConstPtr &msg)
中的ConstPtr是一个长指针,定义在msg文件生成的头文件中。
需要增加修改的地方如下,主要是增加生成自定义massage文件以及生成头文件的宏:
find_package(catkin REQUIRED COMPONENTS
message_generation
roscpp
rospy
std_msgs
)#生成自定义massage文件
add_message_files(
FILES
gps.msg
)#添加上自定义的msg
generate_messages(
DEPENDENCIES
std_msgs
)#用于生成msg对应的头文件
catkin_package(
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
)
include_directories(
include
${catkin_INCLUDE_DIRS}
)
add_executable(talker src/talker.cpp )#必须添加add_dependencies,否则找不到自定义的msg产生的头文件
add_dependencies(talker topic_demo_generate_messages_cpp)#添加依赖,必须有此句以生成msg
target_link_libraries(talker ${catkin_LIBRARIES})#链接
add_executable(listener src/listener.cpp )
add_dependencies(listener topic_demo_generate_messages_cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
个人笔记:需要将cpp文件放在src文件中,如果不在src中,要修改 src/listener.cpp
增加
message_generation
message_runtime
个人笔记:在Ubuntu18.04中使用的时候,package.xml中有细微区别
同topic_demo的创建方式
同topic_demo
vi Greeting.srv
srv中的内容如下:
string name
int32 age
---
string feedback
个人笔记:生成的头文件有:/Greeting.h /GreetingRequest.h /GreetingResponse.h
// This is the C++ version server file of the service demo
//
// 加载必要文件,注意Service_demo的加载方式
# include "ros/ros.h"
# include "service_demo/Greeting.h"
# include "string"
// 定义请求处理函数
bool handle_function(service_demo::Greeting::Request &req,
service_demo::Greeting::Response &res)
{
// 此处我们对请求直接输出
ROS_INFO("Request from %s with age %d ", req.name.c_str(), req.age);
// 返回一个反馈,将response设置为"..."
res.feedback = "Hi " + req.name + ". I'm server!";
return true;
}
int main(int argc, char **argv)
{
// 初始化节点,命名为"greetings_server"
ros::init(argc, argv, "greetings_server");
// 定义service的server端,service名称为“greetings”,收到request请求之后传递给handle_function进行处理
ros::NodeHandle nh;
ros::ServiceServer service = nh.advertiseService("greetings", handle_function);
// 调用可
ros::spin();
return 0;
}
// This is client of the service demo
// 包含必要文件,注意Service文件的包含方式,我们定义的srv文件为Greeting.srv,在包含时需要写成Greeting.h
# include "ros/ros.h"
# include "service_demo/Greeting.h"
int main(int argc, char **argv)
{
// 初始化,节点命名为"greetings_client"
ros::init(argc, argv, "greetings_client");
// 定义service客户端,service名字为“greetings”,service类型为Service_demo
ros::NodeHandle nh;
ros::ServiceClient client = nh.serviceClient("greetings");
// 实例化srv,设置其request消息的内容,这里request包含两个变量,name和age,见Greeting.srv
service_demo::Greeting srv;
srv.request.name = "HAN";
srv.request.age = 20;
if (client.call(srv))
{
// 注意我们的response部分中的内容只包含一个变量response,另,注意将其转变成字符串
ROS_INFO("Response from server: %s", srv.response.feedback.c_str());
}
else
{
ROS_ERROR("Failed to call service Service_demo");
return 1;
}
return 0;
}
同topic_demo的修改方式
ros::param
和 ros::NodeHandle
#include
int main(int argc, char **argv){
ros::init(argc, argv, "param_demo");
ros::NodeHandle nh;
int parameter1, parameter2, parameter3, parameter4, parameter5;
//Get Param的三种方法
//① ros::param::get()获取参数“param1”的value,写入到parameter1上
bool ifget1 = ros::param::get("param1", parameter1);
//② ros::NodeHandle::getParam()获取参数,与①作用相同
bool ifget2 = nh.getParam("param2",parameter2);
//③ ros::NodeHandle::param()类似于①和②
//但如果get不到指定的param,它可以给param指定一个默认值(如33333)
nh.param("param3", parameter3, 33333);
if(ifget1)
ROS_INFO("Get param1 = %d", parameter1);
else
ROS_WARN("Didn't retrieve param1");
if(ifget2)
ROS_INFO("Get param2 = %d", parameter2);
else
ROS_WARN("Didn't retrieve param2");
if(nh.hasParam("param3"))
ROS_INFO("Get param3 = %d", parameter3);
else
ROS_WARN("Didn't retrieve param3");
//Set Param的两种方法
//① ros::param::set()设置参数
parameter4 = 4;
ros::param::set("param4", parameter4);
//② ros::NodeHandle::setParam()设置参数
parameter5 = 5;
nh.setParam("param5",parameter5);
ROS_INFO("Param4 is set to be %d", parameter4);
ROS_INFO("Param5 is set to be %d", parameter5);
//Check Param的两种方法
//① ros::NodeHandle::hasParam()
bool ifparam5 = nh.hasParam("param5");
//② ros::param::has()
bool ifparam6 = ros::param::has("param6");
if(ifparam5)
ROS_INFO("Param5 exists");
else
ROS_INFO("Param5 doesn't exist");
if(ifparam6)
ROS_INFO("Param6 exists");
else
ROS_INFO("Param6 doesn't exist");
//Delete Param的两种方法
//① ros::NodeHandle::deleteParam()
bool ifdeleted5 = nh.deleteParam("param5");
//② ros::param::del()
bool ifdeleted6 = ros::param::del("param6");
if(ifdeleted5)
ROS_INFO("Param5 deleted");
else
ROS_INFO("Param5 not deleted");
if(ifdeleted6)
ROS_INFO("Param6 deleted");
else
ROS_INFO("Param6 not deleted");
ros::Rate rate(0.3);
while(ros::ok()){
int parameter = 0;
ROS_INFO("=============Loop==============");
//roscpp中尚未有ros::param::getallparams()之类的方法
if(ros::param::get("param1", parameter))
ROS_INFO("parameter param1 = %d", parameter);
if(ros::param::get("param2", parameter))
ROS_INFO("parameter param2 = %d", parameter);
if(ros::param::get("param3", parameter))
ROS_INFO("parameter param3 = %d", parameter);
if(ros::param::get("param4", parameter))
ROS_INFO("parameter param4 = %d", parameter);
if(ros::param::get("param5", parameter))
ROS_INFO("parameter param5 = %d", parameter);
if(ros::param::get("param6", parameter))
ROS_INFO("parameter param6 = %d", parameter);
rate.sleep();
}
}