参考视频
阿杰的视频很通俗易懂,非常适合小白入门理解概念
APT源,ROS的软件包应用商店_哔哩哔哩_bilibili
bilibili html5 player
学习文档
—ROS简介_SIGES的博客-CSDN博客
Introduction · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程
前期准备工作
虚拟机系统安装
出现了登陆不进去的情况
解决办法:
关闭防火墙
关闭网络
以管理员身份运行
我在执行解决办法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
创建包 右键,依赖roscpp rospy std_msgs
自定义msg
发布者
包含头文件
初始化ros节点
创建节点句柄
创建发布者对象
编写发布逻辑,发布数据
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的区别
数据结构类型不同
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博客_左值引用
遇到的问题
重新安装