ROS基础知识
1. ROS环境搭建
1.1 安装ROS
ROS(Robot Operating System)是一个用于机器人开发的开源框架,提供了许多工具和库来帮助开发者构建复杂的机器人应用。在开始使用ROS之前,首先需要搭建一个合适的开发环境。以下是在Ubuntu 20.04上安装ROS Noetic的步骤:
步骤1:设置源
打开终端,添加ROS的官方软件源:
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
步骤2:安装密钥
安装ROS的密钥以确保软件包的完整性:
sudo apt install curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
步骤3:更新软件包索引
更新软件包索引以确保获取最新的ROS软件包信息:
sudo apt update
步骤4:安装ROS
安装ROS Noetic桌面全版本,包含ROS、rqt、RViz、仿真工具等:
sudo apt install ros-noetic-desktop-full
步骤5:初始化ROS环境
安装ROS环境初始化工具rosdep
,并初始化环境:
sudo rosdep init
rosdep update
步骤6:配置环境变量
将ROS的环境变量添加到系统中,以便在终端中使用ROS命令:
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
步骤7:安装依赖项
安装一些常用的ROS依赖项:
sudo apt install python3-rosinstall python3-rosinstall-generator python3-wstool build-essential
1.2 创建ROS工作空间
在ROS中,工作空间(workspace)是组织和管理项目的目录结构。创建一个ROS工作空间的步骤如下:
步骤1:创建目录
创建一个名为catkin_ws
的工作空间目录:
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
步骤2:初始化工作空间
使用catkin
工具初始化工作空间:
catkin_init_workspace src
步骤3:编译工作空间
编译工作空间,生成可执行文件和库:
cd ~/catkin_ws/
catkin_make
步骤4:配置环境变量
将工作空间的环境变量添加到系统中:
echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc
2. ROS节点和消息
2.1 ROS节点
在ROS中,节点(node)是执行特定功能的可执行程序。节点通过ROS通信机制与其他节点交换数据。以下是一个简单的ROS节点示例,该节点发布一个消息。
示例:创建一个发布消息的节点
-
创建一个新的ROS包
cd ~/catkin_ws/src catkin_create_pkg my_robot_pub_node rospy std_msgs
-
编写发布消息的节点
在
my_robot_pub_node
包的src
目录下创建一个Python文件talker.py
:#!/usr/bin/env python import rospy from std_msgs.msg import String def talker(): # 初始化节点 rospy.init_node('my_robot_talker', anonymous=True) # 创建一个发布者,发布到名为'chatter'的topic pub = rospy.Publisher('chatter', String, queue_size=10) # 设置发布频率 rate = rospy.Rate(1) # 1 Hz while not rospy.is_shutdown(): # 发布消息 message = "Hello, ROS!" rospy.loginfo(message) pub.publish(message) rate.sleep() if __name__ == '__main__': try: talker() except rospy.ROSInterruptException: pass
-
使脚本可执行
chmod +x ~/catkin_ws/src/my_robot_pub_node/src/talker.py
-
编译工作空间
cd ~/catkin_ws catkin_make
-
运行节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_pub_node talker.py
2.2 ROS消息
消息(message)是ROS节点之间通信的基本数据结构。ROS提供了多种消息类型,包括std_msgs
、sensor_msgs
等。
示例:订阅消息
-
创建一个新的ROS包
cd ~/catkin_ws/src catkin_create_pkg my_robot_sub_node rospy std_msgs
-
编写订阅消息的节点
在
my_robot_sub_node
包的src
目录下创建一个Python文件listener.py
:#!/usr/bin/env python import rospy from std_msgs.msg import String def callback(data): # 回调函数,处理接收到的消息 rospy.loginfo("I heard: %s", data.data) def listener(): # 初始化节点 rospy.init_node('my_robot_listener', anonymous=True) # 订阅名为'chatter'的topic rospy.Subscriber('chatter', String, callback) # 保持节点运行 rospy.spin() if __name__ == '__main__': listener()
-
使脚本可执行
chmod +x ~/catkin_ws/src/my_robot_sub_node/src/listener.py
-
编译工作空间
cd ~/catkin_ws catkin_make
-
运行订阅节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_sub_node listener.py
3. ROS服务和客户端
3.1 ROS服务
服务(service)是一种同步的通信机制,允许一个节点请求另一个节点执行特定的任务。服务由服务请求(service request)和服务响应(service response)组成。
示例:创建一个简单的服务
-
定义服务
在
my_robot_service
包的srv
目录下创建一个服务文件AddTwoInts.srv
:int64 a int64 b --- int64 sum
-
创建服务服务器
在
my_robot_service
包的src
目录下创建一个Python文件add_two_ints_server.py
:#!/usr/bin/env python import rospy from my_robot_service.srv import AddTwoInts, AddTwoIntsResponse def handle_add_two_ints(req): # 处理服务请求 result = req.a + req.b rospy.loginfo("Returning [%s + %s = %s]", req.a, req.b, result) return AddTwoIntsResponse(result) def add_two_ints_server(): # 初始化节点 rospy.init_node('add_two_ints_server') # 创建一个服务服务器,服务名为'add_two_ints' s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints) rospy.loginfo("Ready to add two ints.") rospy.spin() if __name__ == "__main__": add_two_ints_server()
-
创建服务客户端
在
my_robot_service
包的src
目录下创建一个Python文件add_two_ints_client.py
:#!/usr/bin/env python import rospy from my_robot_service.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: rospy.logerr("Service call failed: %s", e) if __name__ == "__main__": rospy.init_node('add_two_ints_client') x = 7 y = 8 result = add_two_ints_client(x, y) rospy.loginfo("Result: %s + %s = %s", x, y, result)
-
编译工作空间
cd ~/catkin_ws catkin_make
-
运行服务服务器
source ~/catkin_ws/devel/setup.bash rosrun my_robot_service add_two_ints_server.py
-
运行服务客户端
source ~/catkin_ws/devel/setup.bash rosrun my_robot_service add_two_ints_client.py
3.2 参数服务器
参数服务器(Parameter Server)是ROS中用于存储和管理参数的全局数据库。节点可以通过参数服务器读取和设置参数。
示例:使用参数服务器
-
创建一个新的ROS包
cd ~/catkin_ws/src catkin_create_pkg my_robot_param_node rospy std_msgs
-
编写设置参数的节点
在
my_robot_param_node
包的src
目录下创建一个Python文件set_param.py
:#!/usr/bin/env python import rospy def set_param(): # 初始化节点 rospy.init_node('set_param_node', anonymous=True) # 设置参数 rospy.set_param('my_robot_param', 42) rospy.loginfo("Set parameter 'my_robot_param' to 42") if __name__ == '__main__': set_param()
-
编写读取参数的节点
在
my_robot_param_node
包的src
目录下创建一个Python文件get_param.py
:#!/usr/bin/env python import rospy def get_param(): # 初始化节点 rospy.init_node('get_param_node', anonymous=True) # 读取参数 param = rospy.get_param('my_robot_param', 'default_value') rospy.loginfo("Parameter 'my_robot_param': %s", param) if __name__ == '__main__': get_param()
-
编译工作空间
cd ~/catkin_ws catkin_make
-
运行设置参数的节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_param_node set_param.py
-
运行读取参数的节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_param_node get_param.py
4. ROS话题和消息类型
4.1 话题
话题(topic)是ROS中用于发布和订阅消息的通信通道。一个节点可以发布消息到话题,另一个节点可以订阅该话题并接收消息。
示例:创建自定义消息类型
-
定义消息
在
my_robot_msg
包的msg
目录下创建一个消息文件CustomMessage.msg
:int64 id string name float64 value
-
编译消息
修改
my_robot_msg
包的CMakeLists.txt
文件,添加以下内容:find_package(catkin REQUIRED COMPONENTS rospy std_msgs message_generation ) add_message_files( FILES CustomMessage.msg ) generate_messages( DEPENDENCIES std_msgs ) catkin_package( CATKIN_DEPENDS message_runtime )
-
编译工作空间
cd ~/catkin_ws catkin_make
-
创建发布自定义消息的节点
在
my_robot_msg
包的src
目录下创建一个Python文件custom_talker.py
:#!/usr/bin/env python import rospy from my_robot_msg.msg import CustomMessage def custom_talker(): # 初始化节点 rospy.init_node('custom_talker', anonymous=True) # 创建一个发布者,发布到名为'custom_chatter'的topic pub = rospy.Publisher('custom_chatter', CustomMessage, queue_size=10) # 设置发布频率 rate = rospy.Rate(1) # 1 Hz while not rospy.is_shutdown(): # 创建并发布自定义消息 message = CustomMessage() message.id = 1 message.name = "Custom Message" message.value = 3.14 rospy.loginfo("Publishing: %s", message) pub.publish(message) rate.sleep() if __name__ == '__main__': try: custom_talker() except rospy.ROSInterruptException: pass
-
创建订阅自定义消息的节点
在
my_robot_msg
包的src
目录下创建一个Python文件custom_listener.py
:#!/usr/bin/env python import rospy from my_robot_msg.msg import CustomMessage def callback(data): # 回调函数,处理接收到的消息 rospy.loginfo("Received: %s, %s, %s", data.id, data.name, data.value) def custom_listener(): # 初始化节点 rospy.init_node('custom_listener', anonymous=True) # 订阅名为'custom_chatter'的topic rospy.Subscriber('custom_chatter', CustomMessage, callback) # 保持节点运行 rospy.spin() if __name__ == '__main__': custom_listener()
-
编译工作空间
cd ~/catkin_ws catkin_make
-
运行发布节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_msg custom_talker.py
-
运行订阅节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_msg custom_listener.py
4.2 消息类型
ROS提供了多种消息类型,包括基本数据类型和复杂数据类型。常用的消息类型有std_msgs
、sensor_msgs
、geometry_msgs
等。
示例:使用geometry_msgs
中的Pose
消息
-
创建一个新的ROS包
cd ~/catkin_ws/src catkin_create_pkg my_robot_pose_node rospy geometry_msgs
-
编写发布
Pose
消息的节点在
my_robot_pose_node
包的src
目录下创建一个Python文件pose_talker.py
:#!/usr/bin/env python import rospy from geometry_msgs.msg import Pose def pose_talker(): # 初始化节点 rospy.init_node('pose_talker', anonymous=True) # 创建一个发布者,发布到名为'pose_chatter'的topic pub = rospy.Publisher('pose_chatter', Pose, queue_size=10) # 设置发布频率 rate = rospy.Rate(1) # 1 Hz while not rospy.is_shutdown(): # 创建并发布`Pose`消息 pose = Pose() pose.position.x = 1.0 pose.position.y = 2.0 pose.position.z = 3.0 pose.orientation.x = 0.0 pose.orientation.y = 0.0 pose.orientation.z = 0.0 pose.orientation.w = 1.0 rospy.loginfo("Publishing: %s", pose) pub.publish(pose) rate.sleep() if __name__ == '__main__': try: pose_talker() except rospy.ROSInterruptException: pass
-
编写订阅
Pose
消息的节点在
my_robot_pose_node
包的src
目录下创建一个Python文件pose_listener.py
:#!/usr/bin/env python import rospy from geometry_msgs.msg import Pose def callback(data): # 回调函数,处理接收到的消息 rospy.loginfo("Received Pose: Position (%s, %s, %s), Orientation (%s, %s, %s, %s)", data.position.x, data.position.y, data.position.z, data.orientation.x, data.orientation.y, data.orientation.z, data.orientation.w) def pose_listener(): # 初始化节点 rospy.init_node('pose_listener', anonymous=True) # 订阅名为'pose_chatter'的topic rospy.Subscriber('pose_chatter', Pose, callback) # 保持节点运行 rospy.spin() if __name__ == '__main__': pose_listener()
-
编译工作空间
cd ~/catkin_ws catkin_make
-
运行发布节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_pose_node pose_talker.py
-
运行订阅节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_pose_node pose_listener.py
5. ROS动作(Action)
5.1 动作概念
动作(action)是ROS中用于处理长时间任务的异步通信机制。动作通常用于执行可能需要较长时间才能完成的任务,如导航或抓取物体。
示例:创建一个简单的动作
-
定义动作
在
my_robot_action
包的action
目录下创建一个动作文件Fibonacci.action
:int32 order --- int32[] sequence --- int32 percent_complete
-
编译动作
修改
my_robot_action
包的CMakeLists.txt
文件,添加以下内容:find_package(catkin REQUIRED COMPONENTS rospy actionlib actionlib_msgs message_generation ) add_action_files( FILES Fibonacci.action ) generate_messages( DEPENDENCIES std_msgs actionlib_msgs ) catkin_package( CATKIN_DEPENDS rospy actionlib message_runtime )
-
编译工作空间
cd ~/catkin_ws catkin_make
5.2 创建动作服务器
-
创建一个新的ROS包
cd ~/catkin_ws/src catkin_create_pkg my_robot_action rospy actionlib
-
编写动作服务器
在
my_robot_action
包的src
目录下创建一个Python文件fibonacci_server.py
:#!/usr/bin/env python import rospy import actionlib from my_robot_action.msg import FibonacciAction, FibonacciGoal, FibonacciResult, FibonacciFeedback def fibonacci_server(): # 初始化节点 rospy.init_node('fibonacci_server') # 创建动作服务器 server = actionlib.SimpleActionServer('fibonacci', FibonacciAction, execute_cb=execute_callback, auto_start=False) server.start() rospy.loginfo("Fibonacci Action Server started.") def execute_callback(goal): # 获取目标值 order = goal.order rospy.loginfo("Fibonacci goal received: %d", order) # 生成斐波那契数列 sequence = [0, 1] feedback = FibonacciFeedback() result = FibonacciResult() for i in range(2, order): sequence.append(sequence[i-1] + sequence[i-2]) # 发送反馈 feedback.percent_complete = int((i / order) * 100) server.publish_feedback(feedback) rospy.sleep(1) # 设置结果 result.sequence = sequence server.set_succeeded(result) rospy.loginfo("Fibonacci sequence: %s", sequence) if __name__ == '__main__': fibonacci_server()
-
使脚本可执行
chmod +x ~/catkin_ws/src/my_robot_action/src/fibonacci_server.py
-
编译工作空间
cd ~/catkin_ws catkin_make
-
运行动作服务器
source ~/catkin_ws/devel/setup.bash rosrun my_robot_action fibonacci_server.py
5.3 创建动作客户端
-
编写动作客户端
在
my_robot_action
包的src
目录下创建一个Python文件fibonacci_client.py
:#!/usr/bin/env python import rospy import actionlib from my_robot_action.msg import FibonacciAction, FibonacciGoal def fibonacci_client(): # 初始化节点 rospy.init_node('fibonacci_client') # 创建动作客户端 client = actionlib.SimpleActionClient('fibonacci', FibonacciAction) # 等待动作服务器启动 client.wait_for_server() # 创建目标并发送 goal = FibonacciGoal() goal.order = 10 client.send_goal(goal, done_cb=done_callback, feedback_cb=feedback_callback) # 等待动作完成 client.wait_for_result() return client.get_result() def done_callback(status, result): # 动作完成回调 rospy.loginfo("Action completed with status: %d", status) rospy.loginfo("Result sequence: %s", result.sequence) def feedback_callback(feedback): # 动作反馈回调 rospy.loginfo("Feedback: %d%% complete", feedback.percent_complete) if __name__ == '__main__': try: result = fibonacci_client() rospy.loginfo("Fibonacci sequence: %s", result.sequence) except rospy.ROSInterruptException: pass
-
使脚本可执行
chmod +x ~/catkin_ws/src/my_robot_action/src/fibonacci_client.py
-
编译工作空间
cd ~/catkin_ws catkin_make
-
运行动作客户端
source ~/catkin_ws/devel/setup.bash rosrun my_robot_action fibonacci_client.py
6. ROS参数服务器
6.1 参数服务器概念
参数服务器(Parameter Server)是ROS中用于存储和管理参数的全局数据库。节点可以通过参数服务器读取和设置参数,从而实现配置的动态管理和共享。
6.2 参数服务器的使用
示例:使用参数服务器
-
创建一个新的ROS包
cd ~/catkin_ws/src catkin_create_pkg my_robot_param_node rospy std_msgs
-
编写设置参数的节点
在
my_robot_param_node
包的src
目录下创建一个Python文件set_param.py
:#!/usr/bin/env python import rospy def set_param(): # 初始化节点 rospy.init_node('set_param_node', anonymous=True) # 设置参数 rospy.set_param('my_robot_param', 42) rospy.loginfo("Set parameter 'my_robot_param' to 42") if __name__ == '__main__': set_param()
-
编写读取参数的节点
在
my_robot_param_node
包的src
目录下创建一个Python文件get_param.py
:#!/usr/bin/env python import rospy def get_param(): # 初始化节点 rospy.init_node('get_param_node', anonymous=True) # 读取参数 param = rospy.get_param('my_robot_param', 'default_value') rospy.loginfo("Parameter 'my_robot_param': %s", param) if __name__ == '__main__': get_param()
-
编译工作空间
cd ~/catkin_ws catkin_make
-
运行设置参数的节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_param_node set_param.py
-
运行读取参数的节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_param_node get_param.py
6.3 动态参数管理
ROS还提供了动态参数管理工具dynamic_reconfigure
,允许在运行时动态调整节点的参数。
示例:使用动态参数管理
-
创建一个新的ROS包
cd ~/catkin_ws/src catkin_create_pkg my_robot_dynamic_param_node rospy dynamic_reconfigure
-
定义动态参数配置
在
my_robot_dynamic_param_node
包的cfg
目录下创建一个配置文件DynamicParamConfig.cfg
:#!/usr/bin/env python from dynamic_reconfigure.parameter_generator_catkin import * gen = ParameterGenerator() gen.add("int_param", int_t, 0, "An integer parameter", 5) gen.add("double_param", double_t, 0, "A double parameter", 1.0, 0.0, 10.0) gen.add("str_param", str_t, 0, "A string parameter", "default_value") gen.add("bool_param", bool_t, 0, "A boolean parameter", True) exit(gen.generate("my_robot_dynamic_param_node", "DynamicParamConfig"))
-
编写动态参数管理的节点
在
my_robot_dynamic_param_node
包的src
目录下创建一个Python文件dynamic_param_node.py
:#!/usr/bin/env python import rospy from dynamic_reconfigure.server import Server from my_robot_dynamic_param_node.cfg import DynamicParamConfig def callback(config, level): rospy.loginfo("Reconfigure request: {int_param}, {double_param}, {str_param}, {bool_param}".format(**config)) return config if __name__ == '__main__': rospy.init_node('dynamic_param_node', anonymous=True) srv = Server(DynamicParamConfig, callback) rospy.spin()
-
使脚本可执行
chmod +x ~/catkin_ws/src/my_robot_dynamic_param_node/src/dynamic_param_node.py
-
编译工作空间
cd ~/catkin_ws catkin_make
-
运行动态参数管理节点
source ~/catkin_ws/devel/setup.bash rosrun my_robot_dynamic_param_node dynamic_param_node.py
-
使用
rqt_reconfigure
工具调整参数rosrun rqt_reconfigure rqt_reconfigure
7. ROS可视化工具
7.1 RViz
RViz是ROS中的可视化工具,用于查看机器人传感器数据、轨迹、模型等。以下是如何使用RViz的基本步骤:
-
安装RViz
sudo apt install ros-noetic-rviz
-
启动RViz
rosrun rviz rviz
-
添加显示
在RViz中,可以通过“Add”按钮添加不同的显示类型,如
LaserScan
、Pose
、Marker
等。
7.2 rqt
rqt是一个可扩展的ROS可视化框架,提供了多种插件来帮助开发者进行调试和监控。
-
安装rqt
sudo apt install ros-noetic-rqt
-
启动rqt
rqt
-
使用插件
在rqt中,可以通过“Plugins”菜单选择不同的插件,如
rqt_logger
、rqt_graph
、rqt_console
等。
8. ROS调试和监控
8.1 使用rostopic
命令
rostopic
命令用于查看和调试ROS话题。以下是一些常用的rostopic
命令:
-
查看所有话题
rostopic list
-
查看话题类型
rostopic type /topic_name
-
发布测试消息
rostopic pub /topic_name std_msgs/String "Hello, ROS!"
-
查看话题消息
rostopic echo /topic_name
8.2 使用rosservice
命令
rosservice
命令用于查看和调试ROS服务。以下是一些常用的rosservice
命令:
-
查看所有服务
rosservice list
-
查看服务类型
rosservice type /service_name
-
调用服务
rosservice call /service_name "10"
8.3 使用rqt_graph
工具
rqt_graph
工具用于可视化ROS节点和话题之间的通信关系。启动rqt_graph
:
rqt_graph
8.4 使用rqt_console
工具
rqt_console
工具用于查看ROS节点的输出日志。启动rqt_console
:
rqt_console
9. ROS仿真工具
9.1 Gazebo
Gazebo是一个3D仿真环境,可以用于模拟机器人在各种环境中的行为。以下是在ROS中使用Gazebo的基本步骤:
-
安装Gazebo
sudo apt install ros-noetic-gazebo-ros-pkgs ros-noetic-gazebo-ros
-
启动Gazebo
roslaunch gazebo_ros empty_world.launch
-
添加机器人模型
在Gazebo中,可以通过“Insert”菜单添加不同的机器人模型。
9.2 Stage
Stage是一个2D仿真环境,适合用于简单移动机器人的仿真。以下是在ROS中使用Stage的基本步骤:
-
安装Stage
sudo apt install ros-noetic-stage-ros
-
启动Stage
roslaunch stage_ros stageros.launch
-
加载地图
在Stage中,可以通过配置文件加载不同的地图和机器人模型。
10. ROS常用命令
10.1 启动ROS核心
roscore
10.2 查看ROS节点
rosnode list
10.3 查看ROS话题
rostopic list
10.4 查看ROS服务
rosservice list
10.5 查看ROS参数
rosparam list
10.6 启动节点
rosrun package_name node_name
10.7 启动launch文件
roslaunch package_name launch_file.launch
11. 总结
通过以上内容,我们了解了ROS的基本环境搭建、节点和消息的使用、服务和参数服务器的管理、动作的实现以及各种调试和可视化工具的使用。这些基础知识将帮助我们更好地开发和调试复杂的机器人应用。希望本文能为初学者提供一个全面的入门指南,进一步探索ROS的更多功能和应用。