一.解决 无法打开源文件
出错原因:系统没有找到.h文件对应的路径。
在编写完msg、srv、action文件后,要进行编译(catkin_make) .
编译之后,msg、srv、action会生成相应的.h文件。
其对应的.h文件目录在devel/include/功能包下,如下:
解决方法:将.h文件目录加入json文件中即可。
"${workspaceFolder}/devel/include",
然后保存json文件,就好了。
二.话题
1.案例一
talker.cpp
#include "ros/ros.h"
#include <sstream>
#include "std_msgs/String.h"
int main(int argc, char** argv){
//ROS结点初始化
ros::init(argc, argv, "talker");
//创建节点句柄
ros::NodeHandle nh;
//创建一个publisher,发布名为chatter的topic,消息类型为std_msgs,队列长度为1000
ros::Publisher chatter_pub=nh.advertise<std_msgs::String>("chatter",1000);
//设置循环频率
ros::Rate loop_Rate(10);
int count=0;
while(ros::ok()){
//初始化std_msgs::String类型的消息
std_msgs::String msg;
std::stringstream ss;
ss<<"hello world:"<<count;
msg.data=ss.str();
//发布消息
ROS_INFO("%s",msg.data.c_str());
chatter_pub.publish(msg);
//回调函数:执行一次回调函数,在这里其实没什么用
ros::spinOnce();
//按照循环频率延时
loop_Rate.sleep();
++count;
}
return 0;
}
listener.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
//接收到topic后,会进入回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg){
//打印topic信息
ROS_INFO("I heard [%s]",msg->data.c_str());
}
int main(int argc,char **argv){
//初始化ros节点
ros::init(argc,argv,"listener");
//创建节点句柄
ros::NodeHandle nh;
//创建一个subscribler,订阅topic为“chatter”,注册回调函数chatterCallback
ros::Subscriber sub=nh.subscribe("chatter",1000,chatterCallback);
//循环等待执行回调函数
ros::spin();
return 0;
}
CMakeList.txt
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
2.案例二:自定义msg文件
创建名为msg的文件夹。
定义msg文件,name、sex、age是变量,unknow、male、female是常量:
Person.msg
# 变量
string name
uint8 sex
uint8 age
#常量
uint8 unknow=0
uint8 male=1
uint8 female=2
package.xml 添加功能包依赖:
<!--添加功能包依赖:msg、srv-->
<build_depend>std_msgs</build_depend>
<exec_depend>std_msgs</exec_depend>
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
其中:部分ROS版本中 exec_depend需要改为run_depend
CMakeLists.txt添加编译选项:
find_package(catkin REQUIRED COMPONENTS
message_generation
)
add_message_files(
FILES
Person.msg
)
generate_messages(
DEPENDENCIES
std_msgs
)
catkin_package(
CATKIN_DEPENDS geometry_msgs roscpp std_msgs message_runtime
)
之后catkin_make
查看自己定义的msg:
rosmsg show Person
三.服务
创建名为srv的文件夹。
定义srv文件,a、b是请求数据,sum是服务数据,也就是a、b传给服务器,服务器相加sum传给客户端:
AddTwoInts.srv
#客户端
int64 a
int64 b
---
#服务器
int64 sum
package.xml 添加功能包依赖:
<!--添加功能包依赖:msg、srv-->
<build_depend>std_msgs</build_depend>
<exec_depend>std_msgs</exec_depend>
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
其中:部分ROS版本中 exec_depend需要改为run_depend
CMakeLists.txt添加编译选项:
find_package(catkin REQUIRED COMPONENTS
message_generation
)
add_service_files(
FILES
AddTwoInts.srv
)
catkin_package(
CATKIN_DEPENDS geometry_msgs roscpp std_msgs message_runtime
)
查看自己定义的srv文件:
rossrv show AddTwoInts.srv
client.cpp
#include <cstdlib>
#include "ros/ros.h"
#include "robot_setup_tf/AddTwoInts.h"
int main(int argc,char** argv){
//ROS节点初始化
ros::init(argc,argv,"add_two_ints_client");
//从终端命令行获取两个加数
if(argc!=3){
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
}
//创建节点句柄
ros::NodeHandle nh;
//创建一个client,请求add_two_service
//service消息类型是robot_setup_tf::AddTwoInts
ros::ServiceClient client=nh.serviceClient<robot_setup_tf::AddTwoInts>("add_two_ints");
//创建robot_setup_tf::AddTwoInts类型的消息对象
robot_setup_tf::AddTwoInts srv;
srv.request.a=atoll(argv[1]);
srv.request.b=atoll(argv[2]);
//发布service请求,等待加法运算
if(client.call(srv)){
ROS_INFO("Sum=%ld",(long int)srv.response.sum);
}else{
ROS_INFO("Failed to call service add_two_ints");
return 1;
}
return 0;
}
server.cpp
#include "ros/ros.h"
#include "robot_setup_tf/AddTwoInts.h"
//回调函数,client传进a、b,完成加法后返回给sum
bool add(robot_setup_tf::AddTwoInts::Request &req, robot_setup_tf::AddTwoInts::Response &res)
{
res.sum=req.a+req.b;
ROS_INFO("request: x=%ld, y=%ld",(long int)req.a,(long int)req.b);
ROS_INFO("response: sum=%ld",(long int)res.sum);
return true;
}
int main(int argc,char** argv){
//ROS节点初始化
ros::init(argc,argv,"add_two_ints_server");
//创建节点句柄
ros::NodeHandle nh;
//创建一个名为"add_two_ints"的server,注册回调函数为add()
ros::ServiceServer service=nh.advertiseService("add_two_ints",add);
//循环等待回调函数
ROS_INFO("Ready to add two ints");
ros::spin();
return 0;
}
CMakeList.txt
add_executable(server src/server.cpp)
target_link_libraries(server ${catkin_LIBRARIES})
add_dependencies(server ${PROJECT_NAME}_gencpp)
add_executable(client src/client.cpp)
target_link_libraries(client ${catkin_LIBRARIES})
add_dependencies(client ${PROJECT_NAME}_gencpp)
运行:
roscore
rosrun robot_setup_tf server
rosrun robot_setup_tf client 4 5
四.动作
创建名为action文件夹。
创建action文件
DoDishes.action
#goal,定义目标信息,客户端
uint8 dishwasher_id
#Specify which dishwasher we want to use
---
#result,定义结果信息,服务器
uint8 total_dishes_cleaned
---
#feedback,定义周期反馈的消息
float32 percent_complete
package.xml添加功能包依赖
<!--添加功能包依赖:action-->
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
CMakeLists.txt添加编译选项:
find_package(catkin REQUIRED COMPONENTS
actionlib_msgs
actionlib
)
add_action_files(
FILES
DoDishes.action
)
generate_messages(
DEPENDENCIES
actionlib_msgs
)
DoDishes_Client.cpp
#include <actionlib/client/simple_action_client.h>
#include "robot_setup_tf/DoDishesAction.h"
typedef actionlib::SimpleActionClient<robot_setup_tf::DoDishesAction> Client;
//当action完成后会调用该回调函数一次
void doneCb(const actionlib::SimpleClientGoalState& state,const robot_setup_tf::DoDishesResultConstPtr& result){
ROS_INFO("Yay!The dishes are now clean");
ros::shutdown();
}
//当action激活后会调用该回调函数一次
void activeCb(){
ROS_INFO("Goal just went active");
}
//收到feedback后调用该回调函数
void feedbackCb(const robot_setup_tf::DoDishesFeedbackConstPtr& feedback){
ROS_INFO("percent_complete: %f",feedback->percent_complete);
}
int main(int argc,char** argv){
ros::init(argc,argv,"do_dishes_client");
//定义一个客户端
Client client("do_dishes",true);
//等待服务器端
ROS_INFO("Waiting for action server to start.");
client.waitForServer();
ROS_INFO("Action server started,sending goal.");
//创建一个action的goal
robot_setup_tf::DoDishesGoal goal;
goal.dishwasher_id=1;
//发送action的goal给服务器端,并且设置回调函数
client.sendGoal(goal,&doneCb,&activeCb,&feedbackCb);
ros::spin();
return 0;
}
DoDishes_Server.cpp
#include "ros/ros.h"
#include <actionlib/server/simple_action_server.h>
#include "robot_setup_tf/DoDishesAction.h"
typedef actionlib::SimpleActionServer<robot_setup_tf::DoDishesAction> Server;
//收到action的goal后调用该回调函数
void execute(const robot_setup_tf::DoDishesGoalConstPtr& goal,Server* as){
ros::Rate r(1);
robot_setup_tf::DoDishesFeedback feedback;
ROS_INFO("Dishwasher %d is working.",goal->dishwasher_id);
//假设洗盘子的进度,并且按照1hz的频率发布进度feedback
for(int i=1;i<=10;i++){
feedback.percent_complete=i*10;
as->publishFeedback(feedback);
r.sleep();
}
//当action完成后,向客户端返回结果
ROS_INFO("Dishwasher %d finish working.",goal->dishwasher_id);
as->setSucceeded();
}
int main(int argc,char** argv){
ros::init(argc,argv,"do_dishes_server");
ros::NodeHandle n;
//定义一个服务器
Server server(n,"do_dishes",boost::bind(&execute,_1,&server),false);
//服务器开始运行
server.start();
ros::spin();
return 0;
}
CMakeList.txt
add_executable(DoDishes_Client src/DoDishes_Client.cpp)
target_link_libraries( DoDishes_Client ${catkin_LIBRARIES})
add_dependencies(DoDishes_Client ${${PROJECT_NAME}_EXPORTED_TARGETS})
add_executable(DoDishes_Server src/DoDishes_Server.cpp)
target_link_libraries( DoDishes_Server ${catkin_LIBRARIES})
add_dependencies(DoDishes_Server ${${PROJECT_NAME}_EXPORTED_TARGETS})
五.TF编程:小乌龟跟随
turtle_tf_broadcaster.cpp
/*
该例程产生tf数据,并计算、发布turtle2的速度指令
*/
#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <turtlesim/Pose.h>
std::string turtle_name;
void poseCallback(const turtlesim::PoseConstPtr& msg)
{
// 创建tf的广播器
static tf::TransformBroadcaster br;
// 根据乌龟当前的位姿,设置相对于世界坐标系的坐标变换
tf::Transform transform;
transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );
tf::Quaternion q;
q.setRPY(0, 0, msg->theta);
transform.setRotation(q);
// 发布坐标变换
br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));
}
int main(int argc, char** argv)
{
// 初始化ROS节点
ros::init(argc, argv, "my_tf_broadcaster");
// 输入参数作为海龟的名字
if (argc != 2)
{
ROS_ERROR("need turtle name as argument");
return -1;
}
turtle_name = argv[1];
// 订阅海龟的位姿话题
ros::NodeHandle node;
ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);
// 循环等待回调函数
ros::spin();
return 0;
};
turtle_tf_listener.cpp
/**
* 该例程监听tf数据,并计算、发布turtle2的速度指令
*/
#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <geometry_msgs/Twist.h>
#include <turtlesim/Spawn.h>
int main(int argc, char** argv)
{
// 初始化ROS节点
ros::init(argc, argv, "my_tf_listener");
// 创建节点句柄
ros::NodeHandle node;
// 请求产生turtle2
ros::service::waitForService("/spawn");
ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");
turtlesim::Spawn srv;
add_turtle.call(srv);
// 创建发布turtle2速度控制指令的发布者
ros::Publisher turtle_vel = node.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 10);
// 创建tf的监听器
tf::TransformListener listener;
ros::Rate rate(10.0);
while (node.ok())
{
// 获取turtle1与turtle2坐标系之间的tf数据
tf::StampedTransform transform;
try
{
//在tf树中查找等待"/turtle2", "/turtle1"是否存在对应变换,超时时间ros::Duration(3.0),如果不设置超时时间,可能会一直卡死
listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0));
listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);
}
catch (tf::TransformException &ex)
{
ROS_ERROR("%s",ex.what());
ros::Duration(1.0).sleep();
continue;
}
// 根据turtle1与turtle2坐标系之间的位置关系,发布turtle2的速度控制指令
geometry_msgs::Twist vel_msg;
vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(),
transform.getOrigin().x());
vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) +
pow(transform.getOrigin().y(), 2));
turtle_vel.publish(vel_msg);
rate.sleep();
}
return 0;
};
tf_demo.launch
<launch>
<!--海龟仿真器-->
<node pkg="turtlesim" type="turtlesim_node" name="sim"/>
<!--键盘控制-->
<node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
<!--两只海龟的tf广播-->
<node pkg="my_tf" type="turtle_tf_broadcaster" args="/turtle1" name="turtle1_tf_broadcaster"/>
<node pkg="my_tf" type="turtle_tf_broadcaster" args="/turtle2" name="turtle2_tf_broadcaster"/>
<!--监听tf广播,并且控制turtle2移动-->
<node pkg="my_tf" type="turtle_tf_listener" name="listener"/>
</launch>
CMakeLists.txt
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
tf
turtlesim
)
add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})
add_executable(turtle_tf_listener src/turtle_tf_listener.cpp)
target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})
运行:
roslaunch my_tf tf_demo.launch