建立工作空间
工作空间( workspace)是一个存放工程开
发相关文件的文件夹。
- src:代码空间( Source space)
- build:编译空间( Build space)
- devel:开发空间(Development space)
- install:安装空间(instal Space)
创建工作空间
初始化工作空间
mkdir ./ros/catkin_ws/src -p
cd ./ros/catkin_ws/src
catkin_init_workspace
编译工作空间
cd ..
catkin_make
在 工作空间生成二个个文件夹build
和devel
设置环境变量
将工作空间的环境变量注入进终端的环境变量
vi ~/.bashrc(或者.zshrc)
source xxx(工作空间的路径)/devel/setup.bash(或者setup.zsh)
# 使当前终端生效
source ~/.bashrc(.zshrc)
也是在当前终端激活, 但在其他终端无效
source ./devel/setup.bash(或者setup.zsh)
测试环境变量
echo $ROS_PACKAGE_PATH
看当前目录的src
是否在环境变量
创建功能包并编译
cd src
# catkin_create_pkg 包名 依赖1 依赖2...
catkin_create_pkg learning_communication std_msgs rospy roscpp
# 返回编译
cd ..
catkin_make
# 刷新环境变量
source ~/.bashrc(.zshrc)
记住二点:
- 同一个工作空间下,不允许存在同名功能包
- 不同工作空间下,允许存在同名功能包
工作空间的覆盖
- 工作空间的路径依次在ROS_ PACKAGE PATH环境变量中记录
- 新设置的路径在ROS_ PACKAGE PATH中会自动放置在最前端
- 运行时,ROS会优先查找最前端的工作空间中是否存在指定的功能包
- 如果不存在,就顺序向后查找其他工作空间
ROS 通信编程
话题编程
创建发布者
在learning_src/talker.cc
文件
//
// Created by 19842 on 10/21/2019.
//
#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int args, char **argv){
// ros节点初始化
ros::init(args, argv, "talker");
// 创建节点句柄
ros::NodeHandle n;
// 创建第一个publisher, 并发布"chatter"的话题, 消息类型是std_msgs::String
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
// 设置循环频率 100ms
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok()){
std_msgs::String msg;
std::stringstream ss;
ss << "Hello World:: " << count << std::endl;
msg.data = ss.str();
// 发布消息
ROS_INFO("%s", msg.data.c_str());
chatter_pub.publish(msg);
// 循环等待回调函数
ros::spinOnce();
// 按照循环频率延时
loop_rate.sleep();
++count;
}
return 0;
}
创建订阅者
在learning_src/listener.cc
文件
//
// Created by 19842 on 10/21/2019.
//
#include "ros/ros.h"
#include "std_msgs/String.h"
// 接收消息后的回调函数
void chatterCallBack(const std_msgs::String::ConstPtr& msg){
ROS_INFO("I heard: %s", msg->data.c_str());
}
int main(int args, char **argv){
// ros节点初始化
ros::init(args, argv, "listener");
// 创建节点句柄
ros::NodeHandle n;
// 创建第一个subscriber, 并订阅"chatter"的话题, 消息类型是std_msgs::String
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallBack);
ros::spin();
return 0;
}
测试
在工作空间
1.启动roscore
roscore
2. 启动 发布者rosrun learning_communication talker
3. 启动 订阅者rosrun learning_communication listener
4. 发布者显示 [ INFO] [1571668965.105450200]: Hello World:: \d
订阅者显示[ INFO] [1571668960.706512100]: I heard: Hello World:: 377
则成功
自定义话题消息
在包空间创建msg
文件存放消息文件, 新建Person
文件
string name
uint8 sex
uint8 age
uint8 unknown=0
uint8 female=1
uint8 male=2
在package.xml
注入依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
在CmakeLists.txtx
添加编译依赖
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
add_message_files(
FILES
Person.msg
)
generate_messages(DEPENDENCIES std_msgs)
catkin_package(CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
)
返回工作空间重新编译
catkin_make
注意 generat_messages
必须在add_message_files
和catkin_package
之间
执行rosmsg show Person
出现描述文本就证明定义成功
服务编程
下面开始一个官方实例, 演示客户端发送二个数, 服务端返回二数之和
准备工作
包空间中在/src
新建server.cc
和client.cc
在包空间创建msg
文件存放消息文件, 新建Person
文件
# request
uint64 a
uint64 b
---
# response
uint64 sum
注意---
, 这个划分响应体和请求头的参数, 在后面的编程也有体现
在package.xml
注入依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
在CmakeLists.txtx
添加编译依赖
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
add_service_files(FILES AddTwoInts.srv)
generate_messages(DEPENDENCIES std_msgs)
catkin_package(CATKIN_DEPENDS roscpp rospy std_msgs message_runtime)
add_executable(server src/server.cc)
add_executable(client src/client.cc)
target_link_libraries(server
${catkin_LIBRARIES}
)
target_link_libraries(client
${catkin_LIBRARIES}
)
add_dependencies(server ${PROJECT_NAME}_gencpp)
add_dependencies(client ${PROJECT_NAME}_gencpp)
返回工作空间重新编译
catkin_make
通过后开始码
server编程
#include "ros/ros.h"
#include "learning_communication/AddTwoInts.h"
/**
* @brief service的回调函数,
* @param req 输入参数, 服务器的请求参数 与`AddTwoInts.srv`的`---`上方对应
* @param res 输出参数, 服务器的响应参数 与`AddTwoInts.srv`的`---`下方对应
* @return
*/
bool add(learning_communication::AddTwoInts::Request &req,
learning_communication::AddTwoInts::Response &res){
// 可以看出res:响应体 req:请求体
res.sum = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld",(long int)req. a, (long int)req. b);
ROS_INFO("sending back response: %ld]",(long int)res.sum);
return true;
}
int main(int args, char **argv){
// ros节点初始化
ros::init(args, argv, "add_two_ints_server");
// 创建节点句柄
ros::NodeHandle n;
// 创建service, 并注册一个回调函数add
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
ROS_INFO("ready add two ints.");
ros::spin();
return 0;
}
client
//
// Created by 19842 on 10/22/2019.
//
#include "ros/ros.h"
// 在 devel/include/
#include "learning_communication/AddTwoInts.h"
int main(int args, char **argv){
if(args != 3){
ROS_ERROR("ERROR:: Need three arguments");
return 1;
}
// ros节点初始化
ros::init(args, argv, "add_two_ints_client");
// 创建节点句柄
ros::NodeHandle n;
// 创建client
ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>("add_two_ints");
learning_communication::AddTwoInts srv;
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
// call默认堵塞
if(client.call(srv)){
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}else{
ROS_ERROR("ERROR!!");
return 1;
}
return 0;
}
在工作空间, 重新编译并通过后继续
测试
- 启动rosmaster,
roscore
- 启动server,
rosrun learning_communication server
- 测试client,
rosrun learning_communication client 2 3
出现INFO] [1571708221.767366300]: Sum: 5
则成功
动作编程(参数编程)
演示如何自定义动作(洗盘子)
准备工作
包空间中在/src
新建DoDishes_server.cc
和DoDishes_client.cc
在包空间创建action
文件存放消息文件, 新建DoDishes.action
文件
# 定义目标信息 global
uint32 dishwasher_id
# Specify which dishwasher we want to use
---
# 定义结果信息 result
uint32 total_dishes_cleaned
---
# 定义周期反馈的消息 feedback
float32 percent_complete
在package.xml
注入依赖
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
在CmakeLists.txtx
添加编译依赖
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
add_action_files(DIRECTORY action FILES DoDishes.action)
generate_messages(DEPENDENCIES std_msgs actionlib_msgs)
catkin_package(CATKIN_DEPENDS roscpp rospy std_msgs message_runtime)
add_executable(DoDishes_server src/DoDishes_server.cc)
add_executable(DoDishes_client src/DoDishes_client.cc)
target_link_libraries(DoDishes_server
${catkin_LIBRARIES}
)
target_link_libraries(DoDishes_client
${catkin_LIBRARIES}
)
add_dependencies(DoDishes_server ${${PROJECT_NAME}_EXPORTED_TARGETS})
add_dependencies(DoDishes_client ${${PROJECT_NAME}_EXPORTED_TARGETS})
返回工作空间重新编译
catkin_make
DoDishes_server
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include "learning_communication/DoDishesAction.h"
typedef actionlib::SimpleActionServer<learning_communication::DoDishesAction> Server;
// 收到action的goal后调用该回调函数
void execute(const learning_communication::DoDishesGoalConstPtr& goal, Server* as)
{
ros::Rate r(1);
learning_communication::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;
}
DoDishes_client
#include <actionlib/client/simple_action_client.h>
#include "learning_communication/DoDishesAction.h"
typedef actionlib::SimpleActionClient<learning_communication::DoDishesAction> Client;
// 当action完成后会调用该回调函数一次
void doneCb(const actionlib::SimpleClientGoalState& state,
const learning_communication::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 learning_communication::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
learning_communication::DoDishesGoal goal;
goal.dishwasher_id = 1;
// 发送action的goal给服务器端,并且设置回调函数
client.sendGoal(goal, &doneCb, &activeCb, &feedbackCb);
ros::spin();
return 0;
}
测试
- 启动 rosmaster
- 启动 DoDishes_server
- 启动 DoDishes_client
分布式通讯
关键组件
launch文件
通过launch文件来启动和配置多节点(会自动启动rosmaster)
- output: 是否有日志输出
- respawn: 启动失败是否重试
- required: 启动失败整个launch是否结束
- ns: 命名空间
- args: 传入参数
TF坐标变换
TF默认可以保存10
秒钟的坐标关系
通过一个树结构来实现的, 通过广播TF变换插入树, 通过监听TF变换
QT工具箱
如果一个参数更新, 可以让其他引用参数的节点跟着更新, 使我们更好地调试
Rviz可视化平台
Rviz是一款三维可视化工具,可以很好的兼容基于ROS软件框架的机器人平台。
- 在Rviz中,可以使用可扩展标记语言XML对机器人、周围物体等任何实物进行尺寸、质
量、位置、材质、关节等属性的描述,并且在界面中呈现出来。 - 同时,Rviz还可以通过图形化的方式,实时显示机器人传感器的信息、机器人的运动状
态、周围环境的变化等信息。 - 总而言之,Rviz通过机器人模型参数、机器人发布的传感信息等数据,为用户进行所有
可监测信息的图形化显示。用户和开发者也可以在Rviz的控制界面下,通过按钮、滑动
条、数值等方式,控制机器人的行为
GaZeBO物理仿真环境