参考视频

阿杰的视频很通俗易懂,非常适合小白入门理解概念

APT源,ROS的软件包应用商店_哔哩哔哩_bilibili

bilibili html5 player

学习文档

—ROS简介_SIGES的博客-CSDN博客

Introduction · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程

前期准备工作

虚拟机系统安装

出现了登陆不进去的情况

解决办法:

  1. 关闭防火墙
  2. 关闭网络
  3. 以管理员身份运行

我在执行解决办法2后解决问题

虚拟机使用技巧

VirtualBox

实现VirtualBox、VMware和Windows之间的复制黏贴操作_windows和虚拟机可以复制粘贴_MarcyTheLibrarian的博客-CSDN博客

出现的问题

无网络,配置虚拟机的网络

这个很重要

太坑了

比如这样

提示大家一下,可能大家在VMware中安装ubuntu的时候会出现UI界面只显示上半部分、不显示下面的继续和安装键,方法是按住alt+f7之后鼠标拖动即可!方法源自:不死的老兵:终于解决了VMware安装Ubuntu18.04时窗口太小的问题

很重要的一点需要注意,虚拟机是将无线网络模拟成了有线连接。。。。。。

ros学习

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
add_executable(Hello_pub
src/Hello_pub.cpp
)
add_executable(Hello_sub
src/Hello_sub.cpp
)

target_link_libraries(Hello_pub
${catkin_LIBRARIES}
)
target_link_libraries(Hello_sub
${catkin_LIBRARIES}
)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
add_executable(person_talker src/person_talker.cpp)
add_executable(person_listener src/person_listener.cpp)



add_dependencies(person_talker ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(person_listener ${PROJECT_NAME}_generate_messages_cpp)


target_link_libraries(person_talker
${catkin_LIBRARIES}
)
target_link_libraries(person_listener
${catkin_LIBRARIES}
)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
add_executable(AddInts_Server src/AddInts_Server.cpp)
add_executable(AddInts_Client src/AddInts_Client.cpp)


add_dependencies(AddInts_Server ${PROJECT_NAME}_gencpp)
add_dependencies(AddInts_Client ${PROJECT_NAME}_gencpp)


target_link_libraries(AddInts_Server
${catkin_LIBRARIES}
)
target_link_libraries(AddInts_Client
${catkin_LIBRARIES}
)

常用函数

rosrun

5.2rosrun

rosrun 包名 可执行文件名 === 运行指定的ROS节点

比如:rosrun turtlesim turtlesim_node

roscore

5.1roscore

roscore === 是 ROS 的系统先决条件节点和程序的集合, 必须运行 roscore 才能使 ROS 节点进行通信。

roscore 将启动:

  • ros master
  • ros 参数服务器
  • rosout 日志节点

ros::rate.sleep和rate

这两个函数是用来控制发布频率的,控制的是循环的频率,不是消息和服务的发布频率,通过控制循环频率从而可以控制发布频率

ros::Rate rate(50) //定义一个频率

rate.sleep() //休眠

往往ros::Rate rate(50)是写在循环外面的,而rate.sleep()是在循环内的,来控制话题发布的频率。

这个频率是指运行上一次loop.sleep()到下一次loop.sleep()之间保持的时间,通常情况下,代码运行速度比设定的频率要快,所以如果运行到下一次loop.sleep()后未达到0.02s(1/50Hz),则会开始休眠,等到0.02s后再执行下一句程序。

source

Linux中source命令的使用方式_Linux小百科的博客-CSDN博客

spin

spin()和spinOnce()__Leveon的博客-CSDN博客_spin()

ros_info

1.ROS_INFO_STREAM(“Hello ROS”),输出字符串

2.ROS_INFO(“s%”, msg.data.c_str()),输出一个字符串变量

3.ROS_INFO(“I heard: [s%]”, msg.data.c_str()),输出一个字符串变量,这里的中括号不是必须的,输出时会直接显示这个中括号

4.ROS_INFO(“I heard: [s%]”, msg->data.c_str()),输出一个指针变量

5.ROS_INFO(“Publish Person Info: name:%s age:%d sex:%d”,

person_msg.name.c_str(), person_msg.age, person_msg.sex),按数据类型输出

常用工具使用

terminator快捷键

Terminator快捷键汇总_laviolette的博客-CSDN博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//第一部份:关于在同一个标签内的操作
Alt+Up //移动到上面的终端
Alt+Down //移动到下面的终端
Alt+Left //移动到左边的终端
Alt+Right //移动到右边的终端
Ctrl+Shift+O //水平分割终端
Ctrl+Shift+E //垂直分割终端
Ctrl+Shift+Right //在垂直分割的终端中将分割条向右移动
Ctrl+Shift+Left //在垂直分割的终端中将分割条向左移动
Ctrl+Shift+Up //在水平分割的终端中将分割条向上移动
Ctrl+Shift+Down //在水平分割的终端中将分割条向下移动
Ctrl+Shift+S //隐藏/显示滚动条
Ctrl+Shift+F //搜索
Ctrl+Shift+C //复制选中的内容到剪贴板
Ctrl+Shift+V //粘贴剪贴板的内容到此处
Ctrl+Shift+W //关闭当前终端
Ctrl+Shift+Q //退出当前窗口,当前窗口的所有终端都将被关闭
Ctrl+Shift+X //最大化显示当前终端
Ctrl+Shift+Z //最大化显示当前终端并使字体放大
Ctrl+Shift+N or Ctrl+Tab //移动到下一个终端
Ctrl+Shift+P or Ctrl+Shift+Tab //Crtl+Shift+Tab 移动到之前的一个终端

//第二部份:有关各个标签之间的操作
F11 //全屏开关
Ctrl+Shift+T //打开一个新的标签
Ctrl+PageDown //移动到下一个标签
Ctrl+PageUp //移动到上一个标签
Ctrl+Shift+PageDown //将当前标签与其后一个标签交换位置
Ctrl+Shift+PageUp //将当前标签与其前一个标签交换位置
Ctrl+Plus (+) //增大字体
Ctrl+Minus (-) //减小字体
Ctrl+Zero (0) //恢复字体到原始大小
Ctrl+Shift+R //重置终端状态
Ctrl+Shift+G //重置终端状态并clear屏幕
Super+g //绑定所有的终端,以便向一个输入能够输入到所有的终端
Super+Shift+G //解除绑定
Super+t //绑定当前标签的所有终端,向一个终端输入的内容会自动输入到其他终端
Super+Shift+T //解除绑定
Ctrl+Shift+I //打开一个窗口,新窗口与原来的窗口使用同一个进程
Super+i //打开一个新窗口,新窗口与原来的窗口使用不同的进程


Shift+Ctrl+W:关闭当前窗口
Shift+Ctrl+Q:关闭终端
Shift+Ctrl+E:水平方向分割终端
Shift+Ctrl+O:垂直方向分割终端
Alt+Up/Down/Left/Rignt:光标在窗口间移动

概念理解

linux内核

什么是Linux内核? (What Is Linux Kernel?)

Linux is actually not a complete operating system. It is just a kernel. Kernel means the low-level part of the operating system which mainly provides system resources to the applications and high-level services. Linux kernel management resources like RAM, CPU, Disk, Input/Output Graphics, etc. Linux kernel also manages processes and user rights.

Linux实际上不是完整的操作系统。 它只是一个内核。 内核是指操作系统的低级部分,主要为应用程序和高级服务提供系统资源。 Linux内核管理资源,例如RAM,CPU,磁盘,输入/输出图形等。Linux内核还管理进程和用户权限。

泛型

就是一个一个通用函数,其函数返回值和参数类型可以不具体指定,用一个虚拟的类型代替

泛型,实质上就是不使用具体数据类型(例如 int、double、float 等),而是使用一种通用类型来进行程序设计的方法,该方法可以大规模的减少程序代码的编写量,让程序员可以集中精力用于业务逻辑的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//函数模板
template<class T>
T Add01(T a,T b){
return a + b;
}
//普通函数
int Add02(int a,int b){
return a + b;
}

int main(){
int a = 99;
int b = 100;
char c = 'c';

cout << Add01(a,c) << endl;//报错,自动类型推导,不发生隐式转换
cout << Add01<int>(a,c) << endl;//正确,可发生隐式转换
cout << Add02(a,c) << endl;//正确,普通函数:char类型的'c'隐式转换为int类型

return 0;
}


catkin

1.什么是catkin

ROS原始的编译和打包系统是rosbuild,而catkin是现在ROS官方指定的系统。

Catkin扩展了CMake,将 cmake 与 make 指令做了一个封装从而完成整个编译过程的工具。

catkin的优点:

  • 软件包编译后无需安装就可使用
  • 自动生成 find_package() 代码,pkg-config文件
  • 解决了多个软件包构建顺序问题
  • 一次配置,多次使用
  • 跨依赖项目编译
2.catkin软件包的组成

一个Catkin的软件包(package)必须要包括两个文件

catkin_make

刚开始是以catkin_make为代表的命令行工具,主要是封装一系列的cmake命令

catkin_make做了下面的事情:

  • 编译所有在src文件夹下的源文件
  • 产生如库、可执行文件,产生的代码将会被放置在devel空间(包括)setup.*sh files
1
2
3
4
5
6
7
8
9
10
11
12
13
$ cd ~/catkin_ws/src/beginner_tutorials/src

# 添加或者修改源文件

$ cd ~/catkin_ws/src/beginner_tutorials

# 更改CMakeFiles.txt映射到你的源文件更改

$ cd ~/catkin_ws

$ catkin_make -DCMAKE_BUILD_TYPE=Release
# 返回工作目录,使用catkin_make

话题通信

创建vscode文件

创建ros工作空间

1
2
3
4
mkdir -p xxx_ws/src
cd xxx_ws
catkin_make

启动vscode

1
2
cd xxx_ws
code .

创建包 右键,依赖roscpp rospy std_msgs

自定义msg

发布者
  1. 包含头文件
  2. 初始化ros节点
  3. 创建节点句柄
  4. 创建发布者对象
  5. 编写发布逻辑,发布数据

ros::Publisher pub=nh.advertise

泛型是需要根据数据类型来选择的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "ros/ros.h"
#include "plumbing_pub_sub/person.h"


int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");

ROS_INFO("消息的发布方");
//初始化ros节点
ros::init(argc,argv,"banzhuren");


//创建节点句柄
ros::NodeHandle nh;

//创建发布者对象
ros::Publisher pub=nh.advertise<plumbing_pub_sub::person>("fang",10);

//编写发布逻辑,发布数据

//5.1发布数据
plumbing_pub_sub::person person;
person.name="章三";
person.age=1;
person.height=1.73;

//5.2设置发布的频率
ros::Rate rate(1);


//循环发布数据

while(ros::ok())
{

person.age+=1;

//数据发布
pub.publish(person);



ROS_INFO("发布的消息:%s,%d,%.2f",person.name.c_str(),person.age,person.height);
//休眠
rate.sleep();
ros::spinOnce();
}




return 0;
}

订阅者

1.包含头文件

2.初始化 ROS 节点:命名(唯一)

3.实例化 ROS 句柄

4.实例化 订阅者 对象

5.处理订阅的消息(回调函数)

6.设置循环调用回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "ros/ros.h"
#include "plumbing_pub_sub/person.h"


//回调函数
void doPerson(const plumbing_pub_sub::person::ConstPtr&person){
ROS_INFO("订阅人的信息%s,%d,%.2f",person->name.c_str(),person->age,person->height);

}//回调函数





int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
ROS_INFO("订阅方实现");

ros::init(argc,argv,"jiazhang");

ros::NodeHandle nh;
ros::Subscriber sub=nh.subscribe("fang",10,doPerson);


ros::spin();
return 0;
}

总结,自定义msg与直接使用已经定义好的msg的区别

  1. 数据结构类型不同
  2. cmakelist配置文件中需要add_dependencies(demo3_pub_person ${PROJECT_NAME}_generate_message_cpp)
遇到的问题

在更改文件之后,重新进行编译,接收数据结果还是上次数据的接收(demo5_ws)

问题没有解决,等一段时间问题集中提问

服务通信

cmake补充

find_package

是用于查找当前功能包所依赖的包

1
2
3
4
5
6
7
8
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
# 需要加入 message_generation,必须有 std_msgs

add_service_files(这一步区别于话题通信,其用的是add_message_files指定msg文件

将创建的src文件夹加入,目的是使其在srv目录下寻找srv文件

1
2
3
4
5
add_service_files(
FILES
AddInts.srv
)

generate_messages

指定生成消息文件时的依赖项

srv文件中利用到了标准数据,所以需要std_msgs

1
2
3
4
5
generate_messages(
DEPENDENCIES
std_msgs
)

catkin_package

是当前功能包所依赖的包所依赖的包

1
2
3
4
5
6
7
8
#执行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo02_talker_listener
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)

服务通信

提交申请并处理响应

1.组织请求

2.处理响应

对象在addInts中已经封装过,需要先创建一个addints对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/*
需求:
编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器
服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,
客户端再解析

服务器实现:
1.包含头文件
2.初始化 ROS 节点
3.创建 ROS 句柄
4.创建 服务 对象
5.回调函数处理请求并产生响应
6.由于请求有多个,需要调用 ros::spin()

*/
#include "ros/ros.h"
#include "demo03_server_client/AddInts.h"

// bool 返回值由于标志是否处理成功
bool doReq(demo03_server_client::AddInts::Request& req,
demo03_server_client::AddInts::Response& resp){
int num1 = req.num1;
int num2 = req.num2;

ROS_INFO("服务器接收到的请求数据为:num1 = %d, num2 = %d",num1, num2);

//逻辑处理
if (num1 < 0 || num2 < 0)
{
ROS_ERROR("提交的数据异常:数据不可以为负数");
return false;
}

//如果没有异常,那么相加并将结果赋值给 resp
resp.sum = num1 + num2;
return true;


}

int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// 2.初始化 ROS 节点
ros::init(argc,argv,"AddInts_Server");
// 3.创建 ROS 句柄
ros::NodeHandle nh;
// 4.创建 服务 对象
ros::ServiceServer server = nh.advertiseService("AddInts",doReq);
ROS_INFO("服务已经启动....");
// 5.回调函数处理请求并产生响应
// 6.由于请求有多个,需要调用 ros::spin()
ros::spin();
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*
需求:
编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器
服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,
客户端再解析

服务器实现:
1.包含头文件
2.初始化 ROS 节点
3.创建 ROS 句柄
4.创建 客户端 对象
5.请求服务,接收响应

*/
// 1.包含头文件
#include "ros/ros.h"
#include "demo03_server_client/AddInts.h"

int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");

// 调用时动态传值,如果通过 launch 的 args 传参,需要传递的参数个数 +3
if (argc != 3)
// if (argc != 5)//launch 传参(0-文件路径 1传入的参数 2传入的参数 3节点名称 4日志路径)
{
ROS_ERROR("请提交两个整数");
return 1;
}


// 2.初始化 ROS 节点
ros::init(argc,argv,"AddInts_Client");
// 3.创建 ROS 句柄
ros::NodeHandle nh;
// 4.创建 客户端 对象
ros::ServiceClient client = nh.serviceClient<demo03_server_client::AddInts>("AddInts");
//等待服务启动成功
//方式1
ros::service::waitForService("AddInts");
//方式2
// client.waitForExistence();
// 5.组织请求数据
demo03_server_client::AddInts ai;
ai.request.num1 = atoi(argv[1]);
ai.request.num2 = atoi(argv[2]);
// 6.发送请求,返回 bool 值,标记是否成功
bool flag = client.call(ai);
// 7.处理响应
if (flag)
{
ROS_INFO("请求正常处理,响应结果:%d",ai.response.sum);
}
else
{
ROS_ERROR("请求处理失败....");
return 1;
}

return 0;
}

参数通信

c++中循环auto&
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include <iostream>
#include <vector>
#include <numeric>
#include <string>
using std::vector;

int main()
{

//测试1:auto c, 创建拷贝,每次遍历都是copy一次,不会对str中的元素进行修改
std::string str("str ing1");
for (auto c : str)
{
c+=1;
std::cout<<c; //输出tus!joh2,c每次获取str的元素后,加1,再打印
}
std::cout<<'\n';
std::cout<<str<<'\n'; //这时再看原str字符串,仍然输出str ing1,没变化。

//测试2:auto& c, reference, 可以通过c给str2中的元素赋值
std::string str2("str ing1");
for (auto& c : str2)
{
c+=1;
std::cout<<c; //输出tus!joh2,c每次获取str的元素后,加1,再打印
}
std::cout<<'\n';
std::cout<<str2<<'\n'; //这时再看原str2字符串,输出tus!joh2,被重新赋值了。

//测试3:auto&& c,右值引用, 可以修改str3中的元素
std::string str3("str ing1");
for (auto&& c : str3)
{
c+=1;
std::cout<<c; //输出tus!joh2,c每次获取str的元素后,加1,再打印
}
std::cout<<'\n';
std::cout<<str3<<'\n'; //这时再看原str3字符串,输出tus!joh2,被重新赋值了。

//测试4:const auto&, 只读str4中的元素
std::string str4("str ing1");
for (const auto& c : str4)
{
//c+=1; //报错,assignment of read-only reference 'c',const限定c只读
std::cout<<c;
}
std::cout<<'\n';
std::cout<<str4<<'\n';
}

拷贝range的元素时,使用for(auto x : range).

for(auto a:b)中b为一个容器,效果是利用a遍历并获得b容器中的每一个值,但是a无法影响到b容器中的元素。

修改range的元素时,使用for(auto && x : range).

for(auto &a:b)中加了引用符号,可以对容器中的内容进行赋值,即可通过对a赋值来做到容器b的内容填充。

只读range的元素时,使用for(const auto & x : range).

for(auto& x : vector),非常量左值引用

当需要对原数据进行同步修改时,就需要添加&,即vector的引用。

会在改变x的同时修改vector。

for(auto&& x : vector),非常量右值引用

当vector返回临时对象,使用auto&会编译错误,临时对象不能绑在non-const l-value reference (非常量左值引用),需使用auto&&(非常量右值引用),初始化右值时也可捕获

左值引用和右值引用

C++中左值(引用)及右值(引用)详解_凹凸曼~的博客-CSDN博客_左值引用

遇到的问题

重新安装