小白的ROS实践学习

目录

一、安装ROS

二、第一个ROS例程(小乌龟仿真)

三、创建工作空间

四、创建和编译功能包

五、ROS通讯编程——话题编程

5.1 发布话题

5.2 订阅话题

5.3 自定义话题消息

1、定义msg文件

 2、在package.xml中添加功能包依赖

3、在CMakeLists.txt添加编译选项

4、查看自定义的消息

六、ROS通讯编程——服务编程

6.1 自定义服务请求应答

1、定义srv文件

2、在package.xml中添加功能包依赖

3、在CMakeLists.txt添加编译选项

6.2 实现服务器

6.3 实现客户端

6.4 运行程序

七、ROS通讯编程——动作编程

7.1 自定义动作信息

1、定义action文件

 2、在package.xml中添加功能包依赖

 3、在CMakeLists.txt添加编译选项

7.2 实现洗盘子动作服务器

7.3 实现洗盘子动作客户端

7.4 运行程序

八、创建一个工作空间以及功能包,在功能包中完成如下工作


一、安装ROS

虚拟机版本:Ubuntu18.04

ROS版本:Melodic

可以使用鱼香ros简易安装。

# 鱼香ros
sudo wget http://fishros.com/install -O fishros && . fishros

二、第一个ROS例程(小乌龟仿真)

roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key

每执行一句命令后,快捷键 ctrl + shift + t 开启新的终端窗口执行下一句命令。


三、创建工作空间

工作空间(workspace)是一个存放工程开发相关文件的文件夹。

src:代码空间

build:编译空间

devel:开发空间

install:安装空间


## 创建工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace

## 编译工作空间
cd ~/catkin_ws/
catkin_make

## 设置环境变量
### 两种方法(建议先使用永久方法):
#### 一次性方法
source devel/setup.bash
#### 永久方法
sudo echo "source ~/catkin_ws/devel/setup.bash">>~/.bashrc
### 重新加载 .bashrc 文件
source ~/.bashrc

## 检查环境变量
echo $ROS_PACKAGE_PATH

四、创建和编译功能包

通常将实现同一具体功能的程序代码放到一个功能包中。

同一工作空间下,不允许存在同名功能包(不同工作空间允许)。

## 创建功能包
## 语法:catkin_create_pkg <package_name> [depend1] [depend2] [depend3]
cd ~/catkin_ws/src
catkin_create_pkg learning_communication std_msgs rospy roscpp

## 编译功能包
cd ~/catkin_ws
catkin_make
source ~/catkin_ws/devel/setup.bash

catkin_make运行后:


五、ROS通讯编程——话题编程

以下编程内容均使用python代码完成。

5.1 发布话题

cd ~/catkin_ws/src/learning_communication/src
## 创建名为talker_py的python文件,用于发布话题
touch talker_py.py
## 给文件添加执行权限(ls如果py文件没有变绿证明没有执行权限)
chmod 755 talker_py.py

 talker_py.py内容如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rospy  # 导入rospy库
from std_msgs.msg import String  # 从std_msgs消息包导入String消息类型

def talker():
    # 创建一个Publisher对象,发布到名为'chatter'的话题
    # 消息类型为String,队列大小为10
    pub = rospy.Publisher('chatter', String, queue_size=10)
    
    # 初始化ROS节点,节点名为'talker_py'
    # anonymous=True表示为节点名添加一个随机数后缀以避免重名
    rospy.init_node('talker_py', anonymous=True)
    
    # 设置发布频率为5Hz
    rate = rospy.Rate(5)
    
    # 循环发布消息,直到节点关闭
    while not rospy.is_shutdown():
        # 创建一个包含当前时间的字符串消息
        hello_str = "hello world %s" % rospy.get_time()
        
        # 输出日志信息
        rospy.loginfo(hello_str)
        
        # 发布消息
        pub.publish(hello_str)
        
        # 按照之前定义的频率休眠一段时间
        rate.sleep()

# 主函数入口
if __name__ == '__main__':
    try:
        # 调用talker函数
        talker()
    except rospy.ROSInterruptException:
        # 捕捉ROS中断异常并进行处理
        pass

 保存python文件,运行以下命令:

roscore
# 打开新终端
rosrun learning_communication talker_py.py

运行结果如下: 

打开新终端,查看话题列表,发现chatter话题已发布。

# 查看话题列表
rostopic list

5.2 订阅话题

接下来创建话题订阅者,订阅chatter话题:

cd ~/catkin_ws/src/learning_communication/src
## 创建名为talker_py的python文件,用于发布话题
touch listener_py.py
# 添加权限
chmod 755 listener_py.py

listener_py.py内容如下: 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo("I heard %s", data.data)

def listener():

    # 初始化节点
    rospy.init_node('listener_py', anonymous=True)

    # 创建订阅者,订阅名为chatter的话题,消息类型为String,当接收到消息时调用callback函数
    rospy.Subscriber('chatter', String, callback)

    # rospy.spin()调用将使节点持续运行,等待回调函数被调用
    rospy.spin()

if __name__ == '__main__':
    listener()

 保存python文件,运行以下命令:

# 不要关闭之前命令
rosrun learning_communication listener_py.py

运行结果如下: 

5.3 自定义话题消息

如果我们需要提前自定义消息服务(下一部分)内的结构,我们需要:

1、定义msg文件

2、在package.xml中添加功能包依赖

3、在CMakeLists.txt添加编译选项

 例如,我们定义一个Person的消息,里面包含姓名、年龄、身高、性别。

1、定义msg文件

mkdir -p ~/catkin_ws/src/learning_communication/msg
# 使用roscd更方便,可以直接实现功能包目录跳转
roscd learning_communication/msg
touch Person.msg

Person.msg内容:

string name
int32 age
float32 height
bool is_male

 2、在package.xml中添加功能包依赖

打开learning_communication包中的package.xml,添加以下依赖:

  <build_depend>message_generation</build_depend>

  <exec_depend>message_runtime</exec_depend>

添加后:

3、在CMakeLists.txt添加编译选项

打开learning_communication包中的CMakeLists.txt,添加编译选项:

# (1)
find_package(
  ......
  ## 添加
  message_generation
)

# (2)
## 添加
add_message_files(
  FILES
  Person.msg
)

# (3)
## 添加
catkin_package(CATKIN_DEPENDS 
  roscpp
  rospy
  std_msgs
  message_runtime
)

添加后:

4、查看自定义的消息

rosmsg show Person


六、ROS通讯编程——服务编程

本部分要实现一个ROS服务客户端节点,用于传入两个整数,并打印出它们的和。

6.1 自定义服务请求应答

1、定义srv文件

2、在package.xml中添加功能包依赖

3、在CMakeLists.txt添加编译选项

1、定义srv文件

mkdir -p ~/catkin_ws/src/learning_communication/srv
# 使用roscd更方便,可以直接实现功能包目录跳转
roscd learning_communication/srv
touch AddTwoInts.srv

AddTwoInts.srv内容: 

int64 a
int64 b
---
int64 sum

2、在package.xml中添加功能包依赖

按照 2、在package.xml中添加功能包依赖 添加依赖即可

3、在CMakeLists.txt添加编译选项

## 添加
add_service_files(
  FILES
  AddTwoInts.srv
)

6.2 实现服务器

cd ~/catkin_ws/src/learning_communication/src
touch add_two_server_py.py
# 添加权限
chmod 755 add_two_server_py.py

add_two_server_py.py内容:

#!/usr/bin/env python

from __future__ import print_function
import rospy
from learning_communication.srv import AddTwoInts, AddTwoIntsResponse

def handle_add_two_ints(req):
    print("Returning [%s + %s = %s]" % (req.a, req.b, (req.a + req.b)))
    return AddTwoIntsResponse(req.a + req.b)

def add_two_ints_server():
    rospy.init_node('add_two_server_py')
    s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)
    print("Ready to add two ints.")
    rospy.spin()

if __name__ == "__main__":
    add_two_ints_server()

6.3 实现客户端

cd ~/catkin_ws/src/learning_communication/src
touch add_two_client_py.py
# 添加权限
chmod 755 add_two_client_py.py

 add_two_client_py内容:

#!/usr/bin/env python

from __future__ import print_function
import sys
import rospy
from learning_communication.srv import AddTwoInts

def add_two_ints_client(x, y):
    rospy.wait_for_service('add_two_ints')
    try:
        add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)
        resp = add_two_ints(x, y)
        return resp.sum
    except rospy.ServiceException as e:
        print("Service call failed: %s" % e)

if __name__ == "__main__":
    if len(sys.argv) == 3:
        x = int(sys.argv[1])
        y = int(sys.argv[2])
    else:
        print("%s [x y]" % sys.argv[0])
        sys.exit(1)
    print("Requesting %s+%s" % (x, y))
    print("%s + %s = %s" % (x, y, add_two_ints_client(x, y)))

6.4 运行程序

roscore
# 打开新终端
rosrun learning_communication add_two_server_py.py
# 打开新终端
rosrun learning_communication add_two_client_py 27 3

运行结果如下 :

 Server节点启动后的日志信息

 Client启动后发布服务请求,并成功接收到反馈结果

Server接收到服务调用后完成加法,将结果反馈给Client


七、ROS通讯编程——动作编程

动作编程相较于服务编程,其动作是异步的,适用于复杂任务处理

该部分要完成一个洗盘子功能。首先需要实现一个动作服务器,处理洗盘子的任务,并实时反馈进度;还需实现一个动作客户端,发送洗盘子的任务目标给服务器,并接收进度反馈和最终结果。

7.1 自定义动作信息

1、定义action文件

mkdir -p ~/catkin_ws/src/learning_communication/action
# 使用roscd更方便,可以直接实现功能包目录跳转
roscd learning_communication/action
touch WashDishes.action

WashDishes.action内容: 

# Example action definition for WashDishes.action

# Define the goal
uint32 total_dishes

---

# Define the result
uint32 washed_dishes

---

# Define the feedback
uint32 percent_complete

 2、在package.xml中添加功能包依赖

打开learning_communication包中的package.xml,添加以下依赖:

  <build_depend>actionlib</build_depend>
  <build_depend>actionlib_msgs</build_depend>

  <exec_depend>actionlib</exec_depend>
  <exec_depend>actionlib_msgs</exec_depend>

添加后:

 3、在CMakeLists.txt添加编译选项

打开learning_communication包中的CMakeLists.txt,添加编译选项:

# (1)
find_package(
  ......
  ## 添加
  actionlib_msgs
  actionlib
)

# (2)
## 添加
add_action_files(
  FILES
  WashDishes.action
)

# (3)
## 添加
generate_messages(
  DEPENDENCIES
  std_msgs
  actionlib_msgs
)

# (4)
catkin_package(
  ......
  ## 添加
  actionlib_msgs
)

添加后:

7.2 实现洗盘子动作服务器

cd ~/catkin_ws/src/learning_communication/src
touch wash_dishes_server_py.py
# 添加权限
chmod 755 wash_dishes_server_py.py

wash_dishes_server_py.py内容如下: 

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import rospy
import actionlib
from learning_communication.msg import WashDishesAction, WashDishesGoal, WashDishesFeedback, WashDishesResult

class WashDishesServer:
    def __init__(self):
        self._as = actionlib.SimpleActionServer('wash_dishes', WashDishesAction, execute_cb=self.execute_cb, auto_start=False)
        self._as.start()
        rospy.loginfo("洗盘子动作服务器已启动.")

    def execute_cb(self, goal):
        rospy.loginfo("收到洗盘子请求,洗 %d 个盘子", goal.total_dishes)
        
        feedback = WashDishesFeedback()
        result = WashDishesResult()

        if self._as.is_preempt_requested():
            rospy.loginfo('目标被取消,停止洗盘子任务.')
            self._as.set_preempted()
            return

        for i in range(1, goal.total_dishes + 1):
            if self._as.is_preempt_requested():
                rospy.loginfo('目标被取消,停止洗盘子任务.')
                self._as.set_preempted()
                return
            
            feedback.percent_complete = i * 100.0 / goal.total_dishes
            self._as.publish_feedback(feedback)
            rospy.loginfo("洗盘子 %d", i)
            rospy.sleep(1.0)
        
        result.washed_dishes = goal.total_dishes
        self._as.set_succeeded(result)
        rospy.loginfo("洗盘子任务完成,共洗了 %d 个盘子", result.washed_dishes)

if __name__ == '__main__':
    rospy.init_node('wash_dishes_server')
    server = WashDishesServer()
    rospy.spin()

7.3 实现洗盘子动作客户端

cd ~/catkin_ws/src/learning_communication/src
touch wash_dishes_client_py.py
# 添加权限
chmod 755 wash_dishes_client_py.py

wash_dishes_client_py.py内容如下: 

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import rospy
import actionlib
from learning_communication.msg import WashDishesAction, WashDishesGoal, WashDishesFeedback

class WashDishesActionClient:
    def __init__(self):
        self._ac = actionlib.SimpleActionClient('wash_dishes', WashDishesAction)
        self._ac.wait_for_server()
        rospy.loginfo("等待动作服务器启动...")
        rospy.loginfo("动作服务器已启动.")

    def send_goal(self, total_dishes):
        goal = WashDishesGoal()
        goal.total_dishes = total_dishes

        rospy.loginfo("发送洗盘子的目标:%d 个盘子", total_dishes)
        self._ac.send_goal(goal, feedback_cb=self.feedback_callback)

    def feedback_callback(self, feedback):
        rospy.loginfo("洗盘子进行中,已洗 %d%% 的盘子", feedback.percent_complete)

    def cancel_goal(self):
        rospy.loginfo("取消洗盘子任务")
        self._ac.cancel_goal()

    def wait_for_result(self):
        self._ac.wait_for_result()
        result = self._ac.get_result()
        if result:
            rospy.loginfo("完成洗盘子任务,共洗了 %d 个盘子", result.washed_dishes)
        else:
            rospy.logwarn("无法获取洗盘子任务的结果")

if __name__ == '__main__':
    rospy.init_node('wash_dishes_client')
    client = WashDishesActionClient()

    while not rospy.is_shutdown():
        num_dishes = raw_input("请输入要洗的盘子数量(或输入 'c' 退出): ")
        if num_dishes.lower() == 'c':
            break

        try:
            num_dishes = int(num_dishes)
            client.send_goal(num_dishes)
        except ValueError:
            rospy.logwarn("无效的输入,请输入一个有效的数字.")
            continue

        while True:
            command = raw_input("输入 'c' 取消任务,或者 'q' 退出等待结果: ")
            if command == 'c':
                client.cancel_goal()
                break

    rospy.loginfo("结束洗盘子任务")

7.4 运行程序

roscore
# 打开新终端
rosrun learning_communication wash_dishes_server_py.py
# 打开新终端
rosrun learning_communication wash_dishes_client_py.py
8

运行结果如下: 

 洗盘子动作服务器运行

洗盘子动作客户端运行 


八、创建一个工作空间以及功能包,在功能包中完成如下工作

话题与服务编程:
(1)通过代码新生成一只海龟,放置在(5,5)点,命名为"turtle2";
(2)通过代码订阅turtle2的实时位置并在终端打印;
(3)控制tuitle2实现运动;
加分项:通过launch启动文件的方式执行。

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值