ROS 入门教程笔记

ROS 入门教程笔记

ROS 入门教程笔记

前言:参看视频:【古月居】古月·ROS入门21讲 | 一学就会的ROS机器人入门教程
如果不是虚拟机的话,可以将文件拷贝到U盘或移动硬盘里,在UNBUNTU 里是可以直接识别的。记得现在windows 系统里先解压,ubuntu 解压会乱码,原因暂时没有深入了解,现以课程学习为主。

1.Linux 系统基础操作

快捷键:

  • 快速打开终端 ctrl + alt + t
  • 关闭终端 ctrl + d

命令行

  • 查看当前路径:pwd
  • 切换路径:cd 返回到上一路径cd …
  • 创建文件夹:mkdir + 文件夹名
  • 查看当前路径的所有文件夹:ls
  • 创建文件:touch + 文件名
  • 剪切文件作用 :mv + 文件名 + 路径
  • 拷贝文件:cp
  • 删除文件:rm 删除目录加 -r
  • 提高权限:sudo
  • 更新软件源列表: sudo apt -get update
  • 命令的使用说明:–help

2.C++&Python 极简基础

区别:

底层:C++
应用层:python

安装

C++ 编译器

sudo apt-get install g++

文件编译

g++ 文件名 -o 编译的文件名

执行文件

./编译文件名

python 解析器


sudo apt-get install python

无需编译,直接执行

python py文件名

3.ROS 是什么

通信机制+开发工具+应用功能+生态系统

通信机制:松耦合分布式通信

开发工具:

  • 命令行&编译器
  • Rviz
  • QT工具箱
  • Gazebo
  • TF坐标变换

应用功能:

  • Navigation
  • SLAM
  • moveit,(初步思想就是搭建积木)。

生态系统:

  • Distribution
  • Repository:开源代码包,不同机构共享软件
  • wiki:ROS主要论坛
  • ROS Answers:咨询问题的网站
  • Blog:相关新闻、图片、视频(http://www.ros.rog/news)

作用:提高机器人研发中的软件服用率

4.ROS 的核心概念

通信机制

Node & ROS Master

节点 Node – 执行单元
  • 完成某个功能的独立运行的文件;
  • 不同节点可以用不同的编程语言,分布式工作;
  • 节点名称必须是唯一的。
节点管理器 ROS Master --控制中心
  • 为节点提供命名和注册服务
  • 辅助节点相互查找、建立链接
  • 提供参数服务器,节点使用服务器存储和检索运行时的参数。

话题通信

话题(topic)–异步通信机制(数据管道)
  • 传输数据的总线
  • 单向:发布者——> 订阅者
  • 多对多,适用数据传输
消息(Message) – 话题数据(数据内容)
  • 数据类型和数据结构
  • 使用.msg 文件定义

服务通信

服务(Service)–同步通信机制(有反馈)
  • 客户端/服务器(C/S)模型,发送/应答
  • 使用 .srv 文件定义请求和应答数据结构
  • 一对多,适用逻辑处理

参数

参数(Parameter) – 全局共享字典
  • 可通过网络访问共享的、多变量的字典(RPC框架)
  • 节点用次服务器来存储和检索运行时的参数
  • 适合静态的,非二进制的,不频繁改变的数据,不合适存储动态配置的数据。

文件系统

功能包

  • 基本单元:节点、消息、服务
功能包清单
  • 记录功能包的基本信息,包含于功能包里面。
元功能包
  • 组织多个用于同一个目的的功能包

5. ROS 命令行工具

常用命令

  • rostopic
  • rosservice
  • rosnode
  • rosparam
  • rosmsg
  • rossrv

小海龟示例

  • roscore:启动ROS Master,所有节点的管理器
  • rosrun turtlesim turtlesim_node:运行节点
  • rosrun turtlesim turtle_teleop_key:按键控制
    工具介绍:
  • rqt_graph :看到系统的全貌,显示不同节点的关系
  • rosnode
    – rosnode list:显示节点,rosout 默认节点,用于显示
    – rosnode info /节点名:显示节点信息
  • rostopic:话题相关
    – rostopic list:显示活跃的topic
    – rostopic pub:给节点发送指令
  • rosmsg :显示消息的具体信息
    – rosmsg show:显示指令的据i定义
  • rosservice:服务相关
    – rosservice call:如创建新对象
  • rosbag :话题记录和复现
    – rosbag record:话题记录
    – rosbag play:话题复现

创建工作空间与功能包

工作空间(Workspace)

存放工程相关的文件夹。

  • src:代码空间
  • build:编译空间。中间文件
  • devel:开发空间(ROS1 有,ROS2 无)
  • install:安装空间,编译生成的可执行文件

创建工作空间

  • 创建工作空间
mkdir -p ~/catkin_ws/src  
cd ~/catkin_ws/src
catkin_init_workspace   //初始变成一个工作空间
  • 编译工作空间
cd .. //回到上一空间
catkin_make //编译生成工作空间
catkin_make install  //产生 install 安装空间
  • 生成功能包
    同个工作空间是不存在多个同名的功能包的
catkin_create_pkg test_pkg std_msgs rospy roscpp //catkin_create_pkg <包名> [依赖的包...],在src文件夹下
cd .. //回到上一级路径
catkin_make  //编译工作空间,将功能包编译
  • 设置环境变量
    要想使用功能包,要设置环境变量。让系统找到工作空间以及对应的功能包
source devel/setup.bash  //devel 文件里面有 setup.up 文件才能找到工作空间和功能包

为了避免每次重复操作,可在主目录下 按快捷键 ctrl + h,显示隐藏文件。 打开 .bashrc 文件
在最后加上 source 路径/devel/setup.bash,就直接加入了环境变量。如source /home/whj/catkin_ws/devel/setup.bash重新打开终端生效。

  • 检查环境变量
echo $ROS_PACKAGE_PATH    //找到所有功能包的路径

src文件的作用

package.xml 文件:
  • xml语言描述功能包相关信息 :名字 、版本号、维护信息 、 许可证等等;
  • 编译和运行所需要的库和包。
CMakeLists.txt:
  • 描述功能包的编译规则;
  • Cmake语法 ,Cmake是基于gc的编译器,通过语言设置编译规则。

6.发布者Publisher的编程实现

topic 需要publisher 和subsciber : 发布和订阅

创建功能包:

catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim

部分代码注释:Velocity_publisher.cpp

  • 节点初始化
  • ROS Master 注册节点信息:创建publisher对象,包含topic名称和发送的数据类型
  • 创建发布数据
  • 按照一定频率循环发布消息。
/**
 * 该例程将发布turtle1/cmd_vel话题,消息类型geometry_msgs::Twist
 */

#include <ros/ros.h>  //ROS 函数定义
#include <geometry_msgs/Twist.h>  //线速度和角速度定义

int main(int argc, char **argv)
{
        // ROS节点初始化
        ros::init(argc, argv, "velocity_publisher");  //ros::init(argc, argv, "节点唯一名称");  argc argv 是属性设置,一般不用

        // 创建节点句柄
        ros::NodeHandle n; //管理节点资源

        // 初始化创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,
        //队列长度10,publisher 发布数据时,底层可能来不及快速响应发布的频率,就先放在队列里面等着。
        //比如无人机发送的频率很快,但是以太网发布的数据很慢,就慢慢发送。
        //注意:底层数据发送能力太弱,就可能出现数据一直发送不出去。此时队列就只会保存最新的一组数据,抛弃原有的 
        //数据,就会出现掉帧的情况
        ros::Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);

        // 设置循环的频率,设置pub的频率
        ros::Rate loop_rate(10);

        int count = 0;
        while (ros::ok())
        {
            // 初始化geometry_msgs::Twist类型的消息,封装数据
                geometry_msgs::Twist vel_msg;
                vel_msg.linear.x = 0.5;
                vel_msg.angular.z = 0.2;

            // 发布消息,通过以太网TCP 的通信机制,将封装好的数据发送出去
                turtle_vel_pub.publish(vel_msg);
                ROS_INFO("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]",
                                vel_msg.linear.x, vel_msg.angular.z);// 输出打印 ,打印发布的信息

            // 按照循环频率延时
            loop_rate.sleep(); 
        }

        return 0;
}

修改功能包下的Cmake文件

#编译 + 链接
add_executable(velocity_publisher src/velocity_publisher.cpp)
# 将后面的文件src/veclocity_publisher.cpp 编译为可执行文件 velocity_publisher
target_link_libraries(velocity_publisher ${catkin_LIBRARIES})
# 将可执行文件 velocity_publisher 和ROS的库做链接,c++接口 py接口 等等

退回到src上一级文件夹,进行编译ctakin_make
编译生成的publisher 在/home/whj/catkin_ws/devel/lib/learning_topic 里可看到 velocity_publisher

测试

分别在三个终端运行三个命令

roscore
rosrun turtlesim turtlesim_node 
rosrun learning_topic velocity_publisher

python的使用

同上,在learning_topic文件下创建 scripts 文件夹,将py文件拷贝过来/自己创建,注意查看权限是否可作为可执行文件(属性查看)。

7.订阅者Subsciber的编程实现

部分代码注释

  • 节点初始化
  • ROS Master 注册节点信息:创建Subscriber对象,订阅需要的话题;
  • 循环等待话题,接收到数据进入回调函数
  • 回调函数处理数据(注意回调函数处理时间不能太长,高效处理,类似中断函数
#include <ros/ros.h>
#include "turtlesim/Pose.h"  // 位置信息

// 接收到订阅的消息后,会进入消息回调函数
void poseCallback(const turtlesim::Pose::ConstPtr& msg)
{
    // 将接收到的消息打印出来
    ROS_INFO("Turtle pose: x:%0.6f, y:%0.6f", msg->x, msg->y);
}

int main(int argc, char **argv)
{
    // 初始化ROS节点
    ros::init(argc, argv, "pose_subscriber");

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

    // 创建一个Subscriber,订阅名为/turtle1/pose的topic,注册回调函数poseCallback
    ros::Subscriber pose_sub = n.subscribe("/turtle1/pose", 10, poseCallback);
    // 订阅的名称 队列长度 回调函数(一旦有数据就会执行,类似中断函数)
    
    // 循环等待回调函数,有数没有数据就一直等待
    ros::spin();

    return 0;
}

修改功能包下的Cmake文件

add_executable(pose_subscriber src/pose_subscriber.cpp)
target_link_libraries(pose_subscriber ${catkin_LIBRARIES})

退回到src上一级文件夹,进行编译ctakin_make

测试

分别在三个终端运行三个命令

roscore
rosrun turtlesim turtlesim_node 
rosrun learning_topic velocity_publisher
rosrun learning_topic pose_subscriber

python的使用

同上,在learning_topic文件下创建 scripts 文件夹,将py文件拷贝过来/自己创建,注意查看权限是否可作为可执行文件(属性查看)。

8. 话题消息的定义和使用

重点时掌握自定义消息(数据信息)的方法和步骤,调用和之前一致

自定义话题信息

  • 定义 msg 文件

    • 1.在功能包里创建 msg 文件夹,存储信息定义信息
    • 2.在该文件下打开终端,创建文件touch Person.msg
    • 3.插入相关代码
      String name
      uint8 sex
      uint8 age
      
      uint8 unknown = 0
      uint8 male    = 1
      uint8 female  = 2
      
  • 在package.xml 中添加功能包依赖
    编译依赖:动态产生message的功能包
    运行依赖:runtime 运行时的依赖

 <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>
  • 在CmakeList 添加编译选项
find_package( …… message_generation)#添加包
add_message_files(FILES Person.msg)# Person.msg  作为定义的消息接口,针对其做编译
generate_messages(DEPENDENCIES std_msgs)# 上一步编译的时候需要依赖 std_msgs 

CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime
# 添加需要的依赖
  • 编译生成语言相关文件
    devel/include/learning_topic 里可以看到编译生成的 .h文件

调用话题信息

整体使用和前面一样

  • 发布信息
#include <ros/ros.h>
#include "learning_topic/Person.h"

int main(int argc, char **argv)
{
    // ROS节点初始化
    ros::init(argc, argv, "person_publisher");

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

    // 创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10
    ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10);

    // 设置循环的频率
    ros::Rate loop_rate(1);

    int count = 0;
    while (ros::ok())
    {
        // 初始化learning_topic::Person类型的消息
    	learning_topic::Person person_msg;
		person_msg.name = "Tom";
		person_msg.age  = 18;
		person_msg.sex  = learning_topic::Person::male;

        // 发布消息
		person_info_pub.publish(person_msg);

       	ROS_INFO("Publish Person Info: name:%s  age:%d  sex:%d", 
				  person_msg.name.c_str(), person_msg.age, person_msg.sex);

        // 按照循环频率延时
        loop_rate.sleep();
    }

    return 0;
}
  • 订阅
#include <ros/ros.h>
#include "learning_topic/Person.h"

// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
    // 将接收到的消息打印出来
    ROS_INFO("Subcribe Person Info: name:%s  age:%d  sex:%d", 
			 msg->name.c_str(), msg->age, msg->sex);
}

int main(int argc, char **argv)
{
    // 初始化ROS节点
    ros::init(argc, argv, "person_subscriber");

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

    // 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
    ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);

    // 循环等待回调函数
    ros::spin();

    return 0;
}

修改 CmakeList文件

add_executable(person_publisher src/person_publisher.cpp)
target_link_libraries(person_publisher ${catkin_LIBRARIES})
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)
add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)

编译执行发布订阅

catkin_make
roscore
rosrun learning_topic person_subscriber
rosrun learning_topic person_publisher

注意:roscore 开启 ROS Master,刚开始起到婚介所的作用,后面建立连接后关闭不影响话题的发布和订阅。

python实现

同上,见代码

9.客户端 client 的实现

创建功能包

catkin_create_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim

代码注释 turtle_spawn.cpp

  • 初始节点
  • 创建client 实例
  • 发布服务请求数据
  • 等待Server处理之后的应答结果
/**
 * 该例程将请求/spawn服务,服务数据类型turtlesim::Spawn
 */

#include <ros/ros.h>
#include <turtlesim/Spawn.h>

int main(int argc, char** argv)
{
    // 初始化ROS节点
	ros::init(argc, argv, "turtle_spawn");

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

    // 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
	ros::service::waitForService("/spawn"); //等待,阻塞型的api,直到确认spawn 服务存在。
	ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn"); //数据类型 + 名字

    // 初始化turtlesim::Spawn的请求数据
	turtlesim::Spawn srv;
	srv.request.x = 2.0;
	srv.request.y = 2.0;
	srv.request.name = "turtle2";

    // 请求服务调用
	ROS_INFO("Call service to spwan turtle[x:%0.6f, y:%0.6f, name:%s]", 
			 srv.request.x, srv.request.y, srv.request.name.c_str());

	add_turtle.call(srv); //阻塞型的数据,直到有反馈

	// 显示服务调用结果
	ROS_INFO("Spwan turtle successfully [name:%s]", srv.response.name.c_str());

	return 0;
};

修改Cmake文件

add_executable(turtle_spawn src/turtle_spawn.cpp)
target_link_libraries(turtle_spawn ${catkin_LIBRARIES})

然后退回到上一级编译catkin_make

编译运行

roscore
rosrun turtlesim turtlesim_node 
rosrun learning_service turtle_spawn

python 实现

流程基本一致

10 服务端Server的编程实现

案例中用的trigger request没有定义,只定义了response。

创建服务器

  • 初始化节点
  • 创建Server实例
  • 循环等待服务请求,进入回调函数
  • 回调函数完成服务功能请求,反馈数据
/**
 * 该例程将执行/turtle_command服务,服务数据类型std_srvs/Trigger
 */
 
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <std_srvs/Trigger.h> //服务数据类型

ros::Publisher turtle_vel_pub;
bool pubCommand = false; // 标志是停止还是运动 false 停止

// service回调函数,输入参数req,输出参数res
bool commandCallback(std_srvs::Trigger::Request  &req,
         			std_srvs::Trigger::Response &res)
{
	pubCommand = !pubCommand;

    // 显示请求数据
    ROS_INFO("Publish turtle velocity command [%s]", pubCommand==true?"Yes":"No");

	// 设置反馈数据
	res.success = true;
	res.message = "Change turtle command state!"

    return true;
}

int main(int argc, char **argv)
{
    // ROS节点初始化
    ros::init(argc, argv, "turtle_command_server");

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

    // 创建一个名为/turtle_command的server,注册回调函数commandCallback,收到请求做处理
    ros::ServiceServer command_service = n.advertiseService("/turtle_command", commandCallback);

	// 创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10
	//给海乌龟发送指令
	turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);

    // 循环等待回调函数
    ROS_INFO("Ready to receive turtle command.");

	// 设置循环的频率
	ros::Rate loop_rate(10);

	while(ros::ok())
	{
		// 查看一次回调函数队列
    	ros::spinOnce();
		
		// 如果标志为true,则发布速度指令
		if(pubCommand)
		{
			geometry_msgs::Twist vel_msg;
			vel_msg.linear.x = 0.5;
			vel_msg.angular.z = 0.2;
			turtle_vel_pub.publish(vel_msg);
		}

		//按照循环频率延时
	    loop_rate.sleep();
	}

    return 0;
}

修改 Cmake

add_executable(turtle_command_server src/turtle_command_server.cpp)
target_link_libraries(turtle_command_server ${catkin_LIBRARIES})

编译后测试

catkin_make
roscore
rosrun turtlesim turtlesim_node 
rosrun learning_service turtle_command_server //此时等待数据
rosservice call /turtlertle_command "{}"   //发送请求,服务器收到请求之后开始控制海龟的运动。


11. 服务数据的定义和使用

服务数据的定义

定义srv 文件

string name
uint8  age
uint8  sex

uint8 unknown = 0
uint8 male    = 1
uint8 female  = 2

---
string result

修改xml 和Cmake文件

操作见pdf

编译

编译之后,文件夹 /home/whj/catkin_ws/devel/include/learning_service 里面有三个 .h 文件

Person.h  //整合
PersonRequest.h   //请求
PersonResponse.h //回复

服务数据的使用

服务端代码

收到数据回复 result ,并打印收到的数据

/**
 * 该例程将执行/show_person服务,服务数据类型learning_service::Person
 */
 
#include <ros/ros.h>
#include "learning_service/Person.h"

// service回调函数,输入参数req,输出参数res
bool personCallback(learning_service::Person::Request  &req,
         			learning_service::Person::Response &res)
{
    // 显示请求数据
    ROS_INFO("Person: name:%s  age:%d  sex:%d", req.name.c_str(), req.age, req.sex);

	// 设置反馈数据
	res.result = "OK";

    return true;
}

int main(int argc, char **argv)
{
    // ROS节点初始化
    ros::init(argc, argv, "person_server");

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

    // 创建一个名为/show_person的server,注册回调函数personCallback
    ros::ServiceServer person_service = n.advertiseService("/show_person", personCallback);

    // 循环等待回调函数
    ROS_INFO("Ready to show person informtion.");
    ros::spin();

    return 0;
}

客户端代码

发送perosn信息请求,收到response 并打印。

/**
 * 该例程将请求/show_person服务,服务数据类型learning_service::Person
 */

#include <ros/ros.h>
#include "learning_service/Person.h"

int main(int argc, char** argv)
{
    // 初始化ROS节点
	ros::init(argc, argv, "person_client");

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

    // 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
	ros::service::waitForService("/show_person");
	ros::ServiceClient person_client = node.serviceClient<learning_service::Person>("/show_person");

    // 初始化learning_service::Person的请求数据
	learning_service::Person srv;
	srv.request.name = "Tom";
	srv.request.age  = 20;
	srv.request.sex  = learning_service::Person::Request::male;

    // 请求服务调用
	ROS_INFO("Call service to show person[name:%s, age:%d, sex:%d]", 
			 srv.request.name.c_str(), srv.request.age, srv.request.sex);

	person_client.call(srv);

	// 显示服务调用结果

配置Cmake 文件

add_executable(person_server src/person_server.cpp)
target_link_libraries(person_server ${catkin_LIBRARIES})
add_dependencies(person_server ${PROJECT_NAME}_gencpp)

add_executable(person_client src/person_client.cpp)
target_link_libraries(person_client ${catkin_LIBRARIES})
add_dependencies(person_client ${PROJECT_NAME}_gencpp)

编译运行

rescore
rosrun learning_service person_client
rosrun learning_service person_server

12. 参数的使用和编程

参数模型

全局变量,通过ROS Master 可以访问一些全局信息。

创建功能包

catkin_create_pkg learning_parameter roscpp rospy std_srvs

参数命令行使用

  • 列出当前参数 rosparam list
  • 显示某个参数值 rosparam get param_key
  • 设置参数值rosparam set param_key param_value,后面需要发送请求,然后服务器会查询相关信息,如rosservice call /clear "{}"
  • 保存参数到文件 rosparam dump param.yaml(文件名)
  • 从文件读取参数rosparam load param.yaml(文件名)
  • 删除参数rosparam delete param_key

代码注释 parameter_config.cpp

  • 初始节点
  • get函数获取参数
  • set函数设置参数
    注意参数的路径要做出修改才有作用。如
	ros::param::get("/turtlesim//background_r", red);
	ros::param::get("/turtlesim//background_g", green);
	ros::param::get("/turtlesim//background_b", blue);
#include <string>
#include <ros/ros.h>
#include <std_srvs/Empty.h>

int main(int argc, char **argv)
{
	int red, green, blue;

    // ROS节点初始化
    ros::init(argc, argv, "parameter_config");

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

    // 读取背景颜色参数
	ros::param::get("/turtlesim//background_r", red);
	ros::param::get("/turtlesim//background_g", green);
	ros::param::get("/turtlesim//background_b", blue);

	ROS_INFO("Get Backgroud Color[%d, %d, %d]", red, green, blue);

	// 设置背景颜色参数
	ros::param::set("/turtlesim//background_r", 255);
	ros::param::set("/turtlesim//background_g", 255);
	ros::param::set("/turtlesim//background_b", 255);

	ROS_INFO("Set Backgroud Color[255, 255, 255]");

    // 读取背景颜色参数
	ros::param::get("/turtlesim//background_r", red);
	ros::param::get("/turtlesim//background_g", green);
	ros::param::get("/turtlesim//background_b", blue);

	ROS_INFO("Re-get Backgroud Color[%d, %d, %d]", red, green, blue);

	// 调用服务,刷新背景颜色
	ros::service::waitForService("/clear");
	ros::ServiceClient clear_background = node.serviceClient<std_srvs::Empty>("/clear");
	std_srvs::Empty srv;
	clear_background.call(srv);
	
	sleep(1);

    return 0;
}

配置 CmakeList 文件

add_executable(parameter_config src/parameter_config.cpp)
target_link_libraries(parameter_config ${catkin_LIBRARIES})

编译运行

catkin_make
roscore
rosrun turtlesim turtlesim_node
rosrun learning_parameter parameter_config

13. ROS 中的坐标系管理系统

机器人中的坐标变换

参考《机器人学导论》

TF 功能包(transform)

  • 10秒内 坐标系之间的关系是怎样的
  • 物体和中心坐标系
  • 中心坐标系和全局坐标系
  • 等等

TF坐标变换如何实现

  • 广播TF变换
  • 监听TF变换

示例

sudo apt-get install ros-noetic-turtle-tf
roslaunch turtle_tf turtle_tf_demo.launch 
rosrun turtlesim turtle_teleop_key 
rosrun tf view_frames  //生成pdf文件显示对应关系
rosrun tf tf_echo turtle1 turtle2  //显示坐标,角度(四元数 、弧度、角度)
rosrun rviz rviz -d 'rospack find turtle_tf'/rviz/turtle_rviz.rviz  //可视化工具的使用

注意事项:

  • 1.注意 ROS 版本
  • 2.运行launch文件出错时,更新pythonsudo apt install python-is-python3
  • 3.rosrun tf view_frames出现问题
File "/opt/ros/noetic/lib/tf/view_frames", line 89, in generate
    m = r.search(vstr)
TypeError: cannot use a string pattern on a bytes-like object

修改办法

sudo vim /opt/ros/noetic/lib/tf/view_frames

第89行代码修改

#m = r.search(vstr)
m = r.search(vstr.decode('utf-8'))

14. TF 坐标系广播和监听的编程实现

实现 launch文件同样的功能

创建功能包

创建广播代码

  • 定义TF 广播器
  • 创建坐标变换值
  • 发布坐标变换
/**
 * 该例程产生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数据
	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);

	// 广播world与海龟坐标系之间的tf数据
	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;
};

创建 TF 监听器

  • 定义 TF 监听器
  • 查找坐标变换
/**
 * 该例程监听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
		{
			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;
};

设置Cmake

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})

编译运行

catkin_make
roscore
rosrun turtlesim turtlesim_node
rosrun learning_tf turtle_tf_broadcaster __name:=turtle1_tf_broadcaster /turtle1  // 节点名 + 坐标系名
rosrun learning_tf turtle_tf_broadcaster __name:=turtle2_tf_broadcaster /turtle2
rosrun learning_tf turtle_tf_listener
rosrun turtlesim turtle_teleop_key 

15. launch 文件的使用方法

可通过 XML 文件实现多节点的配置和启动。自动启动ROS Master

launch文件语法

  • l标签:launch文件中的根元素
  • 标签:
    • pkg:功能包名称
    • type:节点可执行文件名称
    • name:节点运行时的名称
    • 其他可选属性:output 、respawn 、ns等等
  • :设置参数
  • :加载参数
  • : 局部变量
  • :重映射
  • :包含其他launch文件。类似C头文件包含

示例

启动两个节点

将信息output到屏幕上

<launch>
    <node pkg="learning_topic" type="person_subscriber" name="talker" output="screen" />
    <node pkg="learning_topic" type="person_publisher" name="listener" output="screen" /> 
</launch>

参数设置及加载

在节点里面写参数会加上前缀,写在外面就不用。区分

<launch>

	<param name="/turtle_number"   value="2"/>

    <node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node">
		<param name="turtle_name1"   value="Tom"/>
		<param name="turtle_name2"   value="Jerry"/>

		<rosparam file="$(find learning_launch)/config/param.yaml" command="load"/>
	</node>

    <node pkg="turtlesim" type="turtle_teleop_key" name="turtle_teleop_key" output="screen"/>

</launch>

TF 广播和监听的 launch 文件

 <launch>

    <!-- Turtlesim Node-->
    <node pkg="turtlesim" type="turtlesim_node" name="sim"/>
    <node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>

    <node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle1" name="turtle1_tf_broadcaster" />
    <node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle2" name="turtle2_tf_broadcaster" />

    <node pkg="learning_tf" type="turtle_tf_listener" name="listener" />

  </launch>

remap 重映射

话题remap

<launch>

	<include file="$(find learning_launch)/launch/simple.launch" />

    <node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node">
		<remap from="/turtle1/cmd_vel" to="/cmd_vel"/>
	</node>

</launch>

16 常用可视化工具的使用

Qt 工具箱

  • rqt :综合的工具,包括下面所说的的工具。
  • rqt_console: 日志输出工具,可以对信息进行删选处理
  • rqt_graph:计算图可视化工具
  • rqt_plot:数据绘图工具,显示动态变化的数据
  • rqt_image_view:图像渲染工具

Rviz

三维可视化工具,后面请熟悉使用方法,后面会经常使用。

Gazebo

三维物理仿真平台

  • 物理引擎
  • 图形渲染
  • 方便的编程和图形接口
  • 开源免费

应用场景

  • 算法测试
  • 机器人的设计
  • 现实情景下的回溯测试。

17 课程总结和进阶攻略

实现功能

  • 机器人控制和仿真
  • 及时定位与地图建模
  • 运动规划 (move group)
  • 等等

理论基础

  • 机器人学 – 斯坦福大学公开课

资料整理

follow 国内外的一些先进成果

注意事项

  • roscore 每次运行之前都要关闭,然后重新打开。不然程序运行很多的话,某些参数名一样的话,会出现很多奇怪的问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值