参考资料:
3.ros官网教程
以总文件夹P1为例
1.初始化工作空间:
cd P1/src --->>>
catkin_init_workspace
2.编译工作空间
cd /P1 --->>>
catkin_make
3.在src下创建功能包: (右键点击:Create Catkin Package)
命名并指定依赖
名字 + 依赖1 依赖2 依赖3
catkin_create_pkg helloworld rospy roscpp std_msgs
4,基于功能包进行开发:
4.1 C++实现 helloworld 等.功能
// 1. 加入头文件
#include "ros/ros.h"
// 主函数main
int main(int argc, char *argv[])
{
//3.初始化节点
ros::init(argc,argv,"hello_node");
//4.输出日志
ROS_INFO("hello world");
return 0;
}
设置 包/src 下 CMakeLists.txt: 生成可执行文件和需要的动态链接库
###########
## Build ##
###########
add_executable(hello src/helloworld_c.cpp) //构建生成可执行文件,名字尽量一致
arget_link_libraries(hello ${catkin_LIBRARIES}) //设置可执行文件的动态链接库
在P1下用catkin_make进行编译并添加环境变量(或者添加到永久环境变量:
cd P1/
source ./devel/setup.bash
可以通过设置一下命令自动添加环境变量:
nano ~/.bashrc
source ./devel/setup.bash && echo "Command executed successfully" || echo "Command failed"
运行: 包名 程序名
roscore
roscore helloworld hello
4.2 用python实现
在包下文件夹下,创建 scripts 文件夹
mkdir scripts
编辑要执行的helloworld_p.py文件,给予执行权限,并设置CMakeLists.txt文件:
#! /usr/bin/env python
import rospy
if __name__ == "__main__":
rospy.init_node("helo_p")
rospy.loginfo("hello world")
catkin_install_python(PROGRAMS
scripts/helloworld_p.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
后同上
roscore
source ./devel/setup.bash
rosrun helloworld helloworld_p.py
5.配置开发环境
sudo apt install terminator 使用可分布终端
ctrl+alt+t打开终端
安装VScode或者 sublime text
需要安装C/C++扩展,python,ROS,chinese simple扩展包
创建文件时可以用
code .
在终端中用vscode打开文件
用ctrl+shift_B打开 catkin_make:build生成文件
可以用touch .vscode/tasks.json 添加缺少的文件:
{
// 有关 tasks.json 格式的文档,请参见 可以用ctrl+shift+B直接进行编译
// https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"label": "catkin_make:debug", //代表提示的描述性信息
"type": "shell", //可以选择shell或者process,如果是shell代码是在shell里面运行一个命令,如果是process代表作为一个进程来运行
"command": "catkin_make",//这个是我们需要运行的命令
"args": [],//如果需要在命令后面加一些后缀,可以写在这里,比如-DCATKIN_WHITELIST_PACKAGES=“pac1;pac2”
"group": {"kind":"build","isDefault":true},
"presentation": {
"reveal": "always"//可选always或者silence,代表是否输出信息
},
"problemMatcher": "$msCompile"
}
]
}
6.launch文件
roslaunch 包名 节点名字
6.1 包名下创建launch文件夹和 .launch文件
【依然要添加环境变量】
执行:
roslaunch hello_world start_turtlesim.launch
代码规则:
<launch>
<node pkg= "turtlesim" type= "turtlesim_node" name = "turtlesim_GUI"/>
<node pkg= "turtlesim" type= "turtle_teleop_key" name= "turtlesim_key"/>
<node pkg="hello_world" type= "hello_vscode_c" name = "show hello" output="screen"/>
</launch>
目录参考为:
上图
7.ROS架构
查看计算图 rosrun rqt_graph rqt_graph
8.ROS通信机制
ros是进程(Nodes)的分布式框架
三种通信策略:
*话题通信(发布订阅模式)
*服务通信(请求响应模式)
*参数服务器(参数共享模式)
8.1发布实现C++:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream> //拼接字符串
int main(int argc, char * argv[])
{
//初始化ros节点
ros::init(argc,argv,"erGouZi");
//创建节点句柄
ros::NodeHandle nh;
//创建发布者对象
ros::Publisher pub= nh.advertise<std_msgs::String>("House",10);
//发布消息和循环发布数据
std_msgs::String msg;
//设置发布频率
ros::Rate rate(10);
int count=0;
while(ros::ok())
{
//msg.data="hello";
count++;
std::stringstream ss;
ss << "hello No." << count;
msg.data=ss.str();
pub.publish(msg);
ROS_INFO("Publish data is :%s",ss.str()_.c_str());
//频率等待
rate.sleep();
}
return 0;
}
可通过 rostopic 话题名字 echo 获得话题信息
8.2.订阅话题C++
#include "ros/ros.h"
#include "std_msgs/String.h"
//回调函数
void doMessage(const std_msgs::String::ConstPtr &msg)
{
ROS_INFO("Cuihua Subscribered data is : %s",msg->data.c_str());
}
int main(int argc,char ** argv)
{
//初始化节点名称必须不同
//为ROS Master中唯一的,网络拓扑名称
ros::init(argc,argv,"CuiHua");
//实例化节点
ros::NodeHandle nh;
ros::Subscriber sub=nh.subscribe("House",10,doMessage);
ros::spin(); //back to callback function
return 0;
}
发布者也可以通过: ros::Duration(3).sleep();实现短暂延时,防止注册未完成的情况
8.3 发布实现 Python:
#! /usr/bin/env python
import rospy
from std_msgs.msg import String
if __name__ == "__main__":
rospy.init_node("Tom")
pub=rospy.Publisher("Car",String,queue_size=10)
msg=String()
rate = rospy.Rate(1)
count = 0
while not rospy.is_shutdown():
count +=1
msg.data="hello"+str(count)
pub.publish(msg)
rospy.loginfo("publish by pytho is %s",msg.data)
rate.sleep()
pass
8.4 订阅话题 Python
#! /usr/bin/env python
import rospy
from std_msgs.msg import String
def doMessage(msg):
rospy.loginfo("my subscribed data is %s ",msg.data)
if __name__ == "__main__":
rospy.init_node("Jerry")
sub = rospy.Subscriber("Car",String,doMessage,queue_size=10)
rospy.spin()
8.5 自定义话题msgs
8.5.1 常用字段类型:
* int8 int16 int32 int64
*float32 float64
*string
*time duration
*other msg files
*variable_length array[] fixed-length array[C]
8.5.2 创建流程
包下创建 msg文件以及.msg结尾的文件,定义消息类型,如Person.msg
string name
int32 age
float32 height
修改添加package.xml文件下 build_depend 和exec_depend:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
修改CMakeLists.txt文件:
find_package下添加: message_generation
add_message_files添加内容:
add_message_files(
FILES
Person.msg
)
以及generate_messages字段的注释释放:
以及catkin_package下放开CATKIN_DEPENDS coscpp rospy std_msgs........并添加:message_runtime
编译以后可以在 include 和lib中找到Person信息类
并设置c_cpp_properties.json设置include文件
8.5.3 C++订阅使用
#include <ros/ros.h>
#include "plumbing_pub_sub/Person.h"
int main(int argc, char ** argv)
{
ros::init(argc,argv,"Teacher");
ros::NodeHandle nh;
ros::Publisher pub=nh.advertise<plumbing_pub_sub::Person>("CheckStudents",10);
ros::Rate rate(1);
plumbing_pub_sub::Person person;
person.name="LZF";
person.age=99;
person.height=1.99;
while(ros::ok())
{
pub.publish(person);
ROS_INFO("pubed info is:%s,%d,%.02f",person.name.c_str(),person.age,person.height);
person.age++;
rate.sleep();
ros::spinOnce(); //suggest
}
return 0;
}
修改CMakeLists文件需要设置:
add_dependencies(demo02_def_pub ${PROJECT_NAME}_generate_messages_cpp)
编译依赖,使得编译有先后顺序
编译时用rostopic设置,需要添加环境路径
8.5.4. 订阅C++使用
#include <ros/ros.h>
#include "plumbing_pub_sub/Person.h"
void whatIHeard(const plumbing_pub_sub::Person::ConstPtr & msg)
{
ROS_INFO("subed news is : %s,%d,%.6f",msg->name.c_str(),msg->age,msg->height);
}
int main(int argc,char** argv)
{
ros::init(argc,argv,"StuA");
ros::NodeHandle nh;
ros::Subscriber sub=nh.subscribe("CheckStudents",10,whatIHeard);
ros::spin();
return 0;
}
配置同上
8.5.5 发布Python实现:
设置settings.json文件,添加Person信息类路径(第三行):
"python.analysis.extraPaths": [
"/home/lzf20u/Ros_Prj/P1/devel/lib/python3/dist-packages",
"/opt/ros/noetic/lib/python3/dist-packages",
"/home/lzf20u/Ros_Prj/P4_Learning_Ros_2_pub_sub/devel/lib/python3/dist-packages"
]
#! /usr/bin/env python
import rospy
from plumbing_pub_sub.msg import Person
if __name__ == "__main__":
rospy.init_node("OldMan")
pub=rospy.Publisher("Chat",Person,queue_size=10);
p = Person()
p.name="jack"
p.age=10
p.height=1.82
rate=rospy.Rate(1)
while not rospy.is_shutdown():
pub.publish(p)
rospy.loginfo("pub news is %s,%d,%f",p.name,p.age,p.height)
rate.sleep()
pass
同上
8.5.6 订阅Python实现
#! /usr/bin/env python
import rospy
from plumbing_pub_sub.msg import Person
def doPerson(p):
rospy.loginfo("hear info is:%s,%d,%f",p.name,p.age,p.height)
if __name__ == "__main__":
rospy.init_node("HearA")
sub=rospy.Subscriber("Chat",Person,doPerson)
rospy.spin()
8.6 服务通信模型
在包下创建srv文件夹以及srv文件,如AddInts.srv,设置服务消息类型:
#客户端要请求的数据
int32 num1
int32 num2
---
#要发送的数据
int32 sum
配置package.xml文件:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
配置CMakeLists.txt文件:
find_package添加message_generation
以及:
add_service_files(
FILES
AddInts.srv
)
放开注释:
generate_messages(
DEPENDENCIES
std_msgs
)
取消注释,添加messsage_runtime:
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES plumbing_server_client
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
进行编译----
8.6.1.服务端实现:
对c_cpp_properties.json导入path 的devel下include文件:
#include <ros/ros.h>
#include "plumbing_server_client/AddInts.h"
bool doNums(plumbing_server_client::AddInts::Request &request,
plumbing_server_client::AddInts::Response &response)
{
// 接收数据
int num1= request.num1;
int num2= request.num2;
ROS_INFO("Received request data is %d , %d ",num1,num2);
/ 设置返回数据
int sum=num1+num2;
response.sum=sum;
//
return true;
}
int main(int argc ,char ** argv)
{
//初始化节点和句柄,注册服务
ros::init(argc,argv,"Company");
ros::NodeHandle nh;
ros::ServiceServer server=nh.advertiseService("addInts",doNums);
ros::spin();
return 0;
}
设置CMakeLists.txt:
add_dependencies(demo01_server ${PROJECT_NAME}_gencpp)
target_link_libraries(demo01_server
${catkin_LIBRARIES}
)
add_executable(demo01_server src/demo01_server.cpp)
可以通过:rosservice call addInts "num1: 9 num2: 5" 发送请求询问
8.6.2客户端实现
#include <ros/ros.h>
#include <plumbing_server_client/AddInts.h>
int main(int argc , char ** argv)
{
ros::init(argc, argv,"client");
ros::NodeHandle nh;
//订阅服务话题
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("addInts");
plumbing_server_client::AddInts ai;
ai.request.num1=10;
ai.request.num2=99;
//发起请求并响应
bool flag = client.call(ai);
if(flag)
{
ROS_INFO("SUCCESS, SUM= %d",ai.response.sum);
}
return 0;
}
动态输入:
#include <ros/ros.h>
#include <plumbing_server_client/AddInts.h>
int main(int argc , char ** argv)
{
if(argc!=3)
{
ROS_INFO("INPUT ERROR");
return 1;
}
ros::init(argc, argv,"client");
ros::NodeHandle nh;
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("addInts");
plumbing_server_client::AddInts ai;
//ai.request.num1=10;
//ai.request.num2=99;
ai.request.num1=atoi(argv[1]);
ai.request.num2= atoi(argv[2]) ;
//等待服务开始函数
client.waitForExistence();
ros::service::waitForService("addInts");
bool flag = client.call(ai);
if(flag)
{
ROS_INFO("SUCCESS, SUM= %d",ai.response.sum);
}
return 0;
}
8.6.3 Python服务端实现
#! /usr/bin/env python
import rospy
#from plumbing_server_client.srv import AddInts,AddIntsRequest,AddIntsResponse
from plumbing_server_client.srv import *
def answer(request):
num1=request.num1
num2=request.num2
sum=num1+num2
rospy.loginfo("get data num1=%d,num2=%d,sum=%d",num1,num2,sum)
response=AddIntsResponse()
response.sum=sum
return response
if __name__ == "__main__":
rospy.init_node("Company")
server = rospy.Service("addInts",AddInts,answer)
rospy.loginfo("server begin work")
rospy.spin()
pass
可以通过:rosservice call addInts "num1: 0 num2: 5" 发起话题请求
8.6.4 Python客户端实现:
#! /usr/bin/env python
import rospy
from plumbing_server_client.srv import *
if __name__ == "__main__":
rospy.init_node("Client")
client=rospy.ServiceProxy("addInts",AddInts)
response = client.call(12,5)
rospy.loginfo("answered data is %d ",response.sum)
pass
优化:
#! /usr/bin/env python
import rospy
from plumbing_server_client.srv import *
import sys
if __name__ == "__main__":
if(len(sys.argv) != 3:
rospy.loginfo("input error")
sys.exit(1)
num1= int(sys.argv[1])
num2= int(sys.argv[2])
rospy.init_node("Client")
client=rospy.ServiceProxy("addInts",AddInts)
#response = client.call(12,5)
client.wait_for_service()
#or rospy.wait_for_service("addInts")
response = client.call(num1,num2)
rospy.loginfo("answered data is %d ",response.sum)
pass
8.7 参数服务器模型
可使用数据类型:
32-bit integers
boleans
strings
doubles
iso8601 dates
lists
base64-encoded binary data
字典
8.7.1 参数操作 C++ 新增加/修改
代码
#include "ros/ros.h"
/* ros::NodeHandle
setParam()
ros::param
set()
*/
int main(int argc, char ** argv)
{
ros::init(argc,argv,"set_param_c");
ros::NodeHandle nh;
//plan A;
nh.setParam("type","book");
nh.setParam("Distance",11);
//Plan B
ros::param::set("type_param","tom");
ros::param::set("radius_param",0.15);
return 0;
}
rosparam list
和:
rosparam get /Distance可以从服务器查看相关记载数据
运行但不调试
8.7.2 参数操作 C++ 查询:
#include "ros/ros.h"
/*
ros::NodeHandle
param(键,默认值)
存在,返回对应结果,否则返回默认值
getParam(键,存储结果的变量)
存在,返回 true,且将值赋值给参数2
若果键不存在,那么返回值为 false,且不为参数2赋值
getParamCached键,存储结果的变量)--提高变量获取效率
存在,返回 true,且将值赋值给参数2
若果键不存在,那么返回值为 false,且不为参数2赋值
getParamNames(std::vector<std::string>)
获取所有的键,并存储在参数 vector 中
hasParam(键)
是否包含某个键,存在返回 true,否则返回 false
searchParam(参数1,参数2)
搜索键,参数1是被搜索的键,参数2存储搜索结果的变量
ros::param ----- 与 NodeHandle 类似
*/
int main(int argc, char ** argv)
{
ros::init(argc,argv,"get_param");
ros::NodeHandle nh;
//1.param
float Distance= nh.param("Distance",0.5);
ROS_INFO("Distance = %.2f ",Distance);
//2.getParam
double result;
bool flag1 =nh.getParam("Distance",result);
if(flag1)
{
ROS_INFO("distance equals %f: ",result);
}
else
{
ROS_INFO("get error");
}
//3. getParamCached
float result2;
bool flag2=nh.getParamCached("Distance",result2);
//4.getParamNames(std::vector<std::string>)
std::vector<std::string> names;
nh.getParamNames(names);
for (auto &&name : names)
{
ROS_INFO("readed name is %s",name.c_str());
}
//5.hasParam
bool flag3=nh.hasParam("Distance");
bool flag4 = nh.hasParam("diss");
//6.searchParam
std::string keyName;
nh.searchParam("Distance",keyName);
return 0;
}
对于param可以用 ros::param::....来实现,不再赘述
8.7.3 参数操作 Python
添加
#! /usr/bin/env python
import rospy
"""rospy.set_param()
"""
if __name__ == "__main__":
rospy.init_node("param_set_p")
#add
rospy.set_param("type_p","pythonLanguage")
rospy.set_param("Distance_p",88.8)
#change
rospy.set_param("Distance_p",99.9)
pass
获取
#! /usr/bin/env python
import rospy
"""
get_paam_cached
get_param_names
has_param
search_param
"""
if __name__ == "__main__":
rospy.init_node("get_param_p")
Distance = rospy.get_param("Distance_p",0)
#
Distance2=rospy.get_param_cached("Distance",0.5)
#
names=rospy.get_param_names()
for name in names:
rospy.loginfo("names = %s",name)
#
flag2=rospy.has_param("Distance")
#
key = rospy.search_param("Distance")
pass
删除
#! /usr/bin/env python
import rospy
if __name__ == "__name__" :
rospy.init_node("del_param_p")
try:
rospy.delete_param("Distance")
except Exception as e:
rospy.loginfo("doesn't exist")
9.常用命令
rosnode
rostopic
rosservice
rosmsg
rossrv
rosparam
9.1 rosnode ping / list/info /machine /kill /cleanup
9.2 rostopic bw/delay/echo find/hz/info/lsit/pub/type
注意与示例:rostopic pub 话题名 信息类型 信息 ------可以临时发布话题
rostopic up -r 频率 话题 信息类型 信息
9.3 rosservice args/info/call/list/type/uri
注意与示例:rosservice call 服务名 参数
9.4 rosmsg show/info/list/md5/package/packages
rosmsg list |frep -i person
9.5 rossrv show/list/info/md5/package/packages
9.6 rosparam set/get /load/dump/delete/list
10.通信机制实操
记录:要求实现小乌龟运动,圆周的运动
姿态订阅:《添加turtlesim>包 -》xml文件
#include "ros/ros.h"
#include "turtlesim/Pose.h"
void doPose(const turtlesim::Pose::ConstPtr &pose)
{
ROS_INFO("location is:%.2f,%.2f(x,y),forward %.2f,liner speed :%.2f,angel speed %.2f",
pose->x,pose->y,pose->theta,pose->linear_velocity,pose->angular_velocity);
}
int main(int argc, char ** argv)
{
ros::init(argc,argv,"poseSubscriberA");
ros::NodeHandle nh;
ros::Subscriber sub=nh.subscribe("/turtle1/pose",100,doPose);
ros::spin();
return 0;
}
发布位移:
#include "ros/ros.h"
#include "geometry_msgs/Twist.h"
/*
public to ctrl
topic: turtle/cmd_vel
news: geometry_msgs/Twist
*/
int main(int argc, char ** argv)
{
ros::init(argc,argv,"myctrl_pub");
ros::NodeHandle nh;
ros::Publisher pub=nh.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel",10);
ros::Rate rate(10);
geometry_msgs::Twist news;
news.angular.z=10;
news.linear.x=20;
int i=0;
while(ros::ok())
{
pub.publish(news);
rate.sleep();
ros::spinOnce();
i++;
if(i==100)
{
news.angular.z*=-1;
i=1;
}
}
return 0;
}
13.Launch文件解析
13.1 launch标头属性
deprecated = "已经放弃,建议用。。。"
13.2 node标签属性:
pkg
type
name:在ROS网络中的拓扑名称
args = "xxx xxx xxx"
machine="指定运行的机器”
respawn="true |false" 如果节点退出是否重新启动
respawn_delay=“N" 延时启动
required="true | false" 是否必须,如果true,如果该节点退出,将杀死整个roslaunch
ns="xxx" 在指定命名空间中启动节点
clear_param="true | false" 启动前删除节点私有空间的所有参数
output="log | screen" 是否日志输出
13.2 include
将另一个xml格式的launch文件导入当前文件
属性:
file="$(find 包名)/launch/xxx.launch 无空格
ns=”指定命名空间导入" 可选
标签:
env:环境变量设置
arg : 参数传递给被包含的文件
13.3 remap话题重新命名
from ="xxx
to = "xxx"
如:<node pkg="" type =" " name = " " <remap from="" to=”“/> />
13.4 param 服务器添加参数
name="命名空间/参数名” value="xxx" type="str |int |double |yaml|bool"
<param name="param_A" type="int" value="100"/>
可以在node里面,也可以在node外:
<node pkg="turtlesim" type="turtle_teleop_key" name="turtlekey" output="screen">
<param name="param_B" type="int" value="10"/>
</node>
13.5 rosparam 从yaml文件导入参数,或将参数导入到yaml文件
<rospaam>在<node>标签中被视为私有
属性:
command = "load | dump |delete"
file="$(find xxxx)///xxx/yyyy..."
param="参数名称“
ns="命名空间“
例如:
<rosparam command="load" file="$(find launch1)/launch/param.yaml />
13.6 group标签 使节点归属为某个命名空间
属性:
ns="命名空间”
clear_params= "true |false"
13.7 arg参数
属性:
name="参数名称“
default="默认值”
value="数值“
doc="描述”
例如:
<arg name="car_width" default="0.5"/>
<param name="A" value="$(arg car_width)" />
<param name="B" value="$(arg car_width)" />
<param name="C" value="$(arg car_width)" />
并可以通过命令行 传参