ROS(Robot Operating System)是一个用于机器人开发的开源框架,提供了许多工具和库来帮助开发者构建复杂的机器人应用。在开始使用ROS之前,首先需要搭建一个合适的开发环境。以下是在Ubuntu 20.04上安装ROS Noetic的步骤:
打开终端,添加ROS的官方软件源:
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
安装ROS的密钥以确保软件包的完整性:
sudo apt install curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
更新软件包索引以确保获取最新的ROS软件包信息:
sudo apt update
安装ROS Noetic桌面全版本,包含ROS、rqt、RViz、仿真工具等:
sudo apt install ros-noetic-desktop-full
安装ROS环境初始化工具rosdep
,并初始化环境:
sudo rosdep init
rosdep update
将ROS的环境变量添加到系统中,以便在终端中使用ROS命令:
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
安装一些常用的ROS依赖项:
sudo apt install python3-rosinstall python3-rosinstall-generator python3-wstool build-essential
在ROS中,工作空间(workspace)是组织和管理项目的目录结构。创建一个ROS工作空间的步骤如下:
创建一个名为catkin_ws
的工作空间目录:
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
使用catkin
工具初始化工作空间:
catkin_init_workspace src
编译工作空间,生成可执行文件和库:
cd ~/catkin_ws/
catkin_make
将工作空间的环境变量添加到系统中:
echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc
在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
消息(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
服务(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
参数服务器(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
话题(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
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
动作(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
创建一个新的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
编写动作客户端
在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
参数服务器(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
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
RViz是ROS中的可视化工具,用于查看机器人传感器数据、轨迹、模型等。以下是如何使用RViz的基本步骤:
安装RViz
sudo apt install ros-noetic-rviz
启动RViz
rosrun rviz rviz
添加显示
在RViz中,可以通过“Add”按钮添加不同的显示类型,如LaserScan
、Pose
、Marker
等。
rqt是一个可扩展的ROS可视化框架,提供了多种插件来帮助开发者进行调试和监控。
安装rqt
sudo apt install ros-noetic-rqt
启动rqt
rqt
使用插件
在rqt中,可以通过“Plugins”菜单选择不同的插件,如rqt_logger
、rqt_graph
、rqt_console
等。
rostopic
命令rostopic
命令用于查看和调试ROS话题。以下是一些常用的rostopic
命令:
查看所有话题
rostopic list
查看话题类型
rostopic type /topic_name
发布测试消息
rostopic pub /topic_name std_msgs/String "Hello, ROS!"
查看话题消息
rostopic echo /topic_name
rosservice
命令rosservice
命令用于查看和调试ROS服务。以下是一些常用的rosservice
命令:
查看所有服务
rosservice list
查看服务类型
rosservice type /service_name
调用服务
rosservice call /service_name "10"
rqt_graph
工具rqt_graph
工具用于可视化ROS节点和话题之间的通信关系。启动rqt_graph
:
rqt_graph
rqt_console
工具rqt_console
工具用于查看ROS节点的输出日志。启动rqt_console
:
rqt_console
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”菜单添加不同的机器人模型。
Stage是一个2D仿真环境,适合用于简单移动机器人的仿真。以下是在ROS中使用Stage的基本步骤:
安装Stage
sudo apt install ros-noetic-stage-ros
启动Stage
roslaunch stage_ros stageros.launch
加载地图
在Stage中,可以通过配置文件加载不同的地图和机器人模型。
roscore
rosnode list
rostopic list
rosservice list
rosparam list
rosrun package_name node_name
roslaunch package_name launch_file.launch
通过以上内容,我们了解了ROS的基本环境搭建、节点和消息的使用、服务和参数服务器的管理、动作的实现以及各种调试和可视化工具的使用。这些基础知识将帮助我们更好地开发和调试复杂的机器人应用。希望本文能为初学者提供一个全面的入门指南,进一步探索ROS的更多功能和应用。