ROS 基础 什么是 ROS? ROS(Robot Operating System,机器人操作系统)是一个用于编写机器人软件的程序框架,提供了硬件抽象、设备驱动、库、可视化工具、消息传递和包管理等功能。
ROS 的特点:
分布式架构 :节点可以在不同机器上运行
语言无关 :支持 C++、Python 等多种编程语言
开源 :遵循 BSD 许可
丰富的工具 :提供调试、可视化、测试等工具
庞大的社区 :有大量的软件包和库
ROS 的版本 主要版本:
ROS 1 :第一个主要版本,采用分布式架构
ROS 2 :下一代 ROS,改进了实时性、安全性、跨平台支持
ROS 1 版本:
ROS Melodic(推荐用于 Ubuntu 18.04)
ROS Noetic(推荐用于 Ubuntu 20.04)
ROS Kinetic(推荐用于 Ubuntu 16.04)
ROS 的应用 ROS 广泛应用于:
移动机器人
机械臂
无人机
自动驾驶
服务机器人
科研和教育
ROS 架构 ROS 核心概念 1. 节点(Node)
ROS 中的基本执行单元
每个节点负责一个特定的功能
节点之间可以通信
2. 主节点(Master)
管理所有节点
提供命名和注册服务
实现节点之间的发现和通信
3. 参数服务器(Parameter Server)
4. 消息(Message)
ROS 通信机制 ROS 提供了三种主要的通信机制:
话题(Topic) :发布/订阅模式,异步通信
服务(Service) :请求/响应模式,同步通信
动作(Action) :类似服务,但支持长时间任务和取消
ROS 文件系统 工作空间(Workspace)结构:
1 2 3 4 5 6 7 8 9 10 11 workspace/ ├── src/ # 源代码空间 │ └── package1/ # 功能包1 │ ├── CMakeLists.txt │ ├── package.xml │ ├── scripts/ # Python 脚本 │ ├── src/ # C++ 源码 │ └── msg/ # 消息定义 ├── build/ # 编译空间 ├── devel/ # 开发空间 └── install/ # 安装空间
功能包(Package)结构:
1 2 3 4 5 6 7 8 9 package_name/ ├── CMakeLists.txt # C++ 编译配置 ├── package.xml # 包描述文件 ├── scripts/ # Python 脚本 ├── src/ # C++ 源代码 ├── include/ # 头文件 ├── msg/ # 消息定义 ├── srv/ # 服务定义 └── launch/ # 启动文件
ROS 节点(Node) 什么是节点? 节点是 ROS 中执行计算的基本单元。一个节点通常负责一个特定的功能,例如:
节点的特点:
节点是独立的进程
节点可以运行在不同的机器上
节点之间通过话题、服务、动作通信
创建节点(Python) 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 import rospyfrom std_msgs.msg import Stringdef talker (): rospy.init_node('talker' , anonymous=True ) pub = rospy.Publisher('chatter' , String, queue_size=10 ) rate = rospy.Rate(10 ) while not rospy.is_shutdown(): msg = String() msg.data = "Hello ROS" pub.publish(msg) rospy.loginfo(f"Published: {msg.data} " ) rate.sleep() if __name__ == '__main__' : try : talker() except rospy.ROSInterruptException: pass
创建节点(C++) 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 #include <ros/ros.h> #include <std_msgs/String.h> #include <sstream> int main (int argc, char **argv) { ros::init (argc, argv, "talker" ); ros::NodeHandle nh; ros::Publisher pub = nh.advertise <std_msgs::String>("chatter" , 1000 ); ros::Rate loop_rate (10 ) ; int count = 0 ; while (ros::ok ()) { std_msgs::String msg; std::stringstream ss; ss << "Hello ROS " << count; msg.data = ss.str (); pub.publish (msg); ROS_INFO ("%s" , msg.data.c_str ()); ros::spinOnce (); loop_rate.sleep (); ++count; } return 0 ; }
ROS 话题(Topic) 什么是话题? 话题是 ROS 中的发布/订阅通信机制,用于节点之间的异步通信。
话题的特点:
发布/订阅模式 :一个节点发布消息,多个节点可以订阅
异步通信 :发布者和订阅者不需要同时运行
一对多 :一个话题可以有多个发布者和订阅者
单向通信 :数据只能从发布者流向订阅者
话题通信示例 发布者(Publisher):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import rospyfrom std_msgs.msg import Stringdef publisher (): rospy.init_node('publisher_node' ) pub = rospy.Publisher('topic_name' , String, queue_size=10 ) rate = rospy.Rate(1 ) while not rospy.is_shutdown(): msg = String() msg.data = "Hello from publisher" pub.publish(msg) rospy.loginfo("Published: %s" , msg.data) rate.sleep() if __name__ == '__main__' : publisher()
订阅者(Subscriber):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import rospyfrom std_msgs.msg import Stringdef callback (msg ): rospy.loginfo("Received: %s" , msg.data) def subscriber (): rospy.init_node('subscriber_node' ) sub = rospy.Subscriber('topic_name' , String, callback) rospy.spin() if __name__ == '__main__' : subscriber()
话题的相关命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 rostopic list rostopic info /topic_name rostopic type /topic_name rostopic echo /topic_name rostopic pub /topic_name std_msgs/String "data: 'Hello'" rostopic hz /topic_name rostopic bw /topic_name
ROS 服务(Service) 什么是服务? 服务是 ROS 中的请求/响应通信机制,用于节点之间的同步通信。
服务的特点:
请求/响应模式 :客户端发送请求,服务器返回响应
同步通信 :客户端会等待服务器响应
一对一 :一个服务只能有一个服务器,可以有多个客户端
双向通信 :客户端发送请求,服务器返回响应
服务通信示例 服务服务器(Service Server):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import rospyfrom std_srvs.srv import AddTwoInts, AddTwoIntsResponsedef handle_add_two_ints (req ): rospy.loginfo("Request: %d + %d" , req.a, req.b) return AddTwoIntsResponse(req.a + req.b) def server (): rospy.init_node('add_two_ints_server' ) s = rospy.Service('add_two_ints' , AddTwoInts, handle_add_two_ints) rospy.loginfo("Ready to add two ints" ) rospy.spin() if __name__ == '__main__' : server()
服务客户端(Service Client):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import rospyfrom std_srvs.srv import AddTwoIntsdef client (): rospy.init_node('add_two_ints_client' ) rospy.wait_for_service('add_two_ints' ) try : add_two_ints = rospy.ServiceProxy('add_two_ints' , AddTwoInts) response = add_two_ints(1 , 2 ) rospy.loginfo("Sum: %d" , response.sum ) except rospy.ServiceException as e: rospy.logerr("Service call failed: %s" , e) if __name__ == '__main__' : client()
服务的相关命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 rosservice list rosservice info /service_name rosservice type /service_name rosservice call /service_name "arg1: value1 arg2: value2" rosservice args /service_name
ROS 动作(Action) 什么是动作? 动作是 ROS 中的一种通信机制,类似于服务,但适用于长时间运行的任务。
动作的特点:
支持长时间任务 :可以执行需要较长时间的操作
可以取消 :客户端可以取消正在执行的任务
反馈机制 :服务器可以向客户端发送进度反馈
异步通信 :不阻塞客户端
动作的组成:
Goal :目标(客户端发送给服务器)
Feedback :反馈(服务器发送给客户端)
Result :结果(任务完成时服务器发送给客户端)
动作通信示例 动作服务器(Action Server):
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 import rospyimport actionlibfrom actionlib_tutorials.msg import FibonacciAction, FibonacciFeedback, FibonacciResultdef execute_callback (goal ): r = rospy.Rate(1 ) feedback = FibonacciFeedback() result = FibonacciResult() for i in range (1 , goal.order + 1 ): if action_server.is_preempt_requested(): rospy.loginfo('Goal preempted' ) action_server.set_preempted() return feedback.sequence.append(feedback.sequence[i-1 ] + feedback.sequence[i]) action_server.publish_feedback(feedback) r.sleep() result.sequence = feedback.sequence action_server.set_succeeded(result) def action_server (): rospy.init_node('fibonacci_action_server' ) global action_server action_server = actionlib.SimpleActionServer( 'fibonacci' , FibonacciAction, execute_callback, auto_start=False ) action_server.start() rospy.spin() if __name__ == '__main__' : action_server()
动作客户端(Action Client):
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 import rospyimport actionlibfrom actionlib_tutorials.msg import FibonacciAction, FibonacciGoaldef feedback_callback (feedback ): rospy.loginfo("Feedback: %s" , feedback.sequence) def action_client (): rospy.init_node('fibonacci_action_client' ) client = actionlib.SimpleActionClient('fibonacci' , FibonacciAction) client.wait_for_server() goal = FibonacciGoal() goal.order = 10 client.send_goal(goal, feedback_cb=feedback_callback) client.wait_for_result() result = client.get_result() rospy.loginfo("Result: %s" , result.sequence) if __name__ == '__main__' : action_client()
ROS 消息(Message) 什么是消息? 消息是 ROS 中节点之间传递数据的格式定义。消息定义了数据的结构和类型。
消息的特点:
类型定义 :指定了数据的类型和结构
序列化 :可以序列化为网络传输格式
跨语言 :可以在不同语言编写的节点间传递
标准消息类型 常用标准消息类型:
std_msgs/String:字符串
std_msgs/Int32:32位整数
std_msgs/Float64:64位浮点数
geometry_msgs/Twist:速度指令
sensor_msgs/Image:图像数据
nav_msgs/Odometry:里程计数据
自定义消息 创建自定义消息(msg 文件):
1 2 3 4 5 # Person.msg string first_name string last_name uint8 age float64 height
CMakeLists.txt 配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 find_package (catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation ) add_message_files( FILES Person.msg ) generate_messages( DEPENDENCIES std_msgs )
package.xml 配置:
1 2 <build_depend > message_generation</build_depend > <exec_depend > message_runtime</exec_depend >
ROS 参数(Parameter) 什么是参数? 参数是 ROS 中的全局配置数据,存储在参数服务器中。
参数的特点:
全局访问 :所有节点都可以访问
持久化 :可以保存到文件
动态修改 :运行时可以修改
参数操作 设置参数:
1 2 3 4 5 6 7 import rospyrospy.init_node('param_node' ) rospy.set_param('param_name' , 'value' ) rospy.set_param('~private_param' , 'private_value' )
获取参数:
1 2 3 4 5 6 value = rospy.get_param('param_name' , 'default_value' ) if rospy.has_param('param_name' ): value = rospy.get_param('param_name' )
参数的相关命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 rosparam list rosparam get /param_name rosparam set /param_name value rosparam load file.yaml rosparam dump file.yaml rosparam delete /param_name
ROS Launch 文件 什么是 Launch 文件? Launch 文件是 XML 格式的文件,用于同时启动多个节点和相关配置。
Launch 文件的作用:
启动多个节点
设置参数
配置节点命名空间
设置重映射(remap)
Launch 文件示例 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 <launch > <node name ="talker" pkg ="rospy_tutorials" type ="talker" output ="screen" /> <node name ="listener" pkg ="rospy_tutorials" type ="listener" output ="screen" > <param name ="param_name" value ="param_value" /> </node > <node name ="node_name" pkg ="package_name" type ="executable" > <remap from ="old_topic" to ="new_topic" /> </node > <group ns ="robot1" > <node name ="node_name" pkg ="package_name" type ="executable" /> </group > <include file ="$(find package_name)/launch/other.launch" /> <rosparam file ="$(find package_name)/config/params.yaml" command ="load" /> </launch >
运行 Launch 文件:
1 roslaunch package_name launch_file.launch
ROS 工具 常用 ROS 命令 节点相关:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 rosrun package_name node_name rosnode list rosnode info /node_name rosnode info /node_name | grep -E "Publishers|Subscribers" rosnode kill /node_name rosnode ping /node_name
话题相关:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 rostopic list rostopic info /topic_name rostopic echo /topic_name rostopic type /topic_name rostopic pub /topic_name msg_type "field: value" rostopic hz /topic_name
参数相关:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 rosparam list rosparam get /param_name rosparam set /param_name value rosparam load file.yaml rosparam dump file.yaml
包相关:
1 2 3 4 5 6 7 8 9 10 11 rospack find package_name rospack list rospack depends package_name rosmsg show package_name/MessageName
Bag 文件(数据录制和回放):
1 2 3 4 5 6 7 8 9 10 11 rosbag record /topic1 /topic2 rosbag record -a rosbag play file.bag rosbag info file.bag
RViz(可视化工具) RViz 是 ROS 的 3D 可视化工具,用于:
显示机器人模型
可视化传感器数据
显示地图和路径
调试和监控
启动 RViz:
ROS 消息类型详解 geometry_msgs Twist(速度指令):
1 2 3 4 5 6 7 8 9 from geometry_msgs.msg import Twisttwist = Twist() twist.linear.x = 0.5 twist.linear.y = 0.0 twist.linear.z = 0.0 twist.angular.x = 0.0 twist.angular.y = 0.0 twist.angular.z = 0.5
Pose(位姿):
1 2 3 4 5 from geometry_msgs.msg import Pose, Point, Quaternionpose = Pose() pose.position = Point(x=1.0 , y=2.0 , z=0.0 ) pose.orientation = Quaternion(x=0.0 , y=0.0 , z=0.0 , w=1.0 )
sensor_msgs Image(图像):
1 2 3 4 5 6 7 8 9 10 11 12 from sensor_msgs.msg import Imageimport cv2from cv_bridge import CvBridgebridge = CvBridge() def image_callback (msg ): cv_image = bridge.imgmsg_to_cv2(msg, "bgr8" ) cv2.imshow("Image" , cv_image) cv2.waitKey(1 )
LaserScan(激光雷达):
1 2 3 4 5 6 from sensor_msgs.msg import LaserScandef laser_callback (msg ): ranges = msg.ranges angles = msg.angle_min + np.arange(len (ranges)) * msg.angle_increment
nav_msgs Odometry(里程计):
1 2 3 4 5 6 7 8 from nav_msgs.msg import Odometrydef odom_callback (msg ): position = msg.pose.pose.position orientation = msg.pose.pose.orientation linear_vel = msg.twist.twist.linear angular_vel = msg.twist.twist.angular
ROS 坐标系(TF) 什么是 TF? TF(Transform)是 ROS 中的坐标变换系统,用于管理机器人各个部件之间的坐标关系。
TF 的作用:
TF 树(Transform Tree):
1 2 3 4 5 map └── odom └── base_link ├── camera_link └── laser_link
TF 使用示例 发布坐标变换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import rospyimport tf2_rosimport geometry_msgs.msgdef publish_transform (): rospy.init_node('tf_publisher' ) broadcaster = tf2_ros.TransformBroadcaster() transform = geometry_msgs.msg.TransformStamped() transform.header.frame_id = "parent_frame" transform.child_frame_id = "child_frame" transform.transform.translation.x = 1.0 transform.transform.translation.y = 0.0 transform.transform.translation.z = 0.0 transform.transform.rotation.w = 1.0 rate = rospy.Rate(10 ) while not rospy.is_shutdown(): transform.header.stamp = rospy.Time.now() broadcaster.sendTransform(transform) rate.sleep()
监听坐标变换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import rospyimport tf2_rosdef listen_transform (): rospy.init_node('tf_listener' ) tf_buffer = tf2_ros.Buffer() tf_listener = tf2_ros.TransformListener(tf_buffer) rate = rospy.Rate(10 ) while not rospy.is_shutdown(): try : transform = tf_buffer.lookup_transform( 'target_frame' , 'source_frame' , rospy.Time() ) rospy.loginfo("Transform: %s" , transform) except (tf2_ros.LookupException, tf2_ros.ConnectivityException): continue rate.sleep()
TF 相关命令 1 2 3 4 5 6 7 8 9 10 11 rosrun rqt_tf_tree rqt_tf_tree rosrun tf tf_echo parent_frame child_frame rosrun tf view_frames rosrun tf static_transform_publisher x y z qx qy qz qw parent_frame child_frame
ROS 包管理 创建功能包 创建 Python 包:
1 catkin_create_pkg my_package rospy std_msgs
创建 C++ 包:
1 catkin_create_pkg my_package roscpp std_msgs
package.xml 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" ?> <package format ="2" > <name > my_package</name > <version > 1.0.0</version > <description > My ROS package</description > <maintainer email ="user@example.com" > User</maintainer > <license > MIT</license > <buildtool_depend > catkin</buildtool_depend > <build_depend > roscpp</build_depend > <build_depend > rospy</build_depend > <build_depend > std_msgs</build_depend > <exec_depend > roscpp</exec_depend > <exec_depend > rospy</exec_depend > <exec_depend > std_msgs</exec_depend > </package >
CMakeLists.txt 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 cmake_minimum_required (VERSION 3.0 .2 )project (my_package)find_package (catkin REQUIRED COMPONENTS roscpp rospy std_msgs ) catkin_package() include_directories ( ${catkin_INCLUDE_DIRS} ) add_executable (my_node src/my_node.cpp)target_link_libraries (my_node ${catkin_LIBRARIES} )catkin_install_python(PROGRAMS scripts/my_script.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} )
ROS 常用工具和调试 rqt 工具集 rqt_graph(计算图可视化):
1 2 rosrun rqt_graph rqt_graph
rqt_plot(数据绘图):
1 2 rosrun rqt_plot rqt_plot /topic_name/field_name
rqt_console(日志查看):
1 2 rosrun rqt_console rqt_console
rqt_reconfigure(动态参数配置):
1 2 rosrun rqt_reconfigure rqt_reconfigure
Gazebo 仿真 Gazebo 是 ROS 中常用的物理仿真环境。
启动 Gazebo:
在 Gazebo 中加载机器人模型:
1 gazebo worlds/my_world.world
Launch 文件中启动 Gazebo:
1 2 3 4 5 6 7 8 <launch > <include file ="$(find gazebo_ros)/launch/empty_world.launch" > <arg name ="world_name" value ="$(find my_package)/worlds/my_world.world" /> </include > <node name ="spawn_model" pkg ="gazebo_ros" type ="spawn_model" args ="-file $(find my_package)/models/robot.urdf -urdf -model robot" /> </launch >
URDF(机器人模型描述) URDF(Unified Robot Description Format)用于描述机器人的物理结构。
URDF 示例:
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 <?xml version="1.0" ?> <robot name ="my_robot" > <link name ="base_link" > <visual > <geometry > <box size ="0.4 0.4 0.1" /> </geometry > <material name ="blue" > <color rgba ="0 0 1 1" /> </material > </visual > </link > <joint name ="base_to_laser" type ="fixed" > <parent link ="base_link" /> <child link ="laser_link" /> <origin xyz ="0 0 0.05" rpy ="0 0 0" /> </joint > <link name ="laser_link" > <visual > <geometry > <cylinder radius ="0.05" length ="0.1" /> </geometry > </visual > </link > </robot >
查看 URDF 模型:
1 2 3 4 5 6 7 check_urdf robot.urdf rosrun rviz rviz rosrun urdf_tutorial display.launch model:=robot.urdf
ROS 2 简介 ROS 2 vs ROS 1 ROS 2 的改进:
实时性 :支持实时系统
安全性 :DDS 安全机制
跨平台 :支持 Windows、macOS、Linux
分布式 :原生支持分布式系统
更好的性能 :基于 DDS(Data Distribution Service)
ROS 2 核心概念:
Node :节点(类似 ROS 1)
Topic :话题(类似 ROS 1,使用 DDS 实现)
Service :服务(类似 ROS 1)
Action :动作(类似 ROS 1)
Parameter :参数(类似 ROS 1)
Lifecycle :生命周期节点(新特性)
ROS 2 命令行工具:
1 2 3 4 5 6 7 8 9 10 11 12 ros2 node list ros2 node info /node_name ros2 topic list ros2 topic echo /topic_name ros2 topic pub /topic_name msg_type "data: value" ros2 service list ros2 service call /service_name service_type "arg: value"
常见面试题 1. ROS 的基本概念是什么? 答案: ROS(Robot Operating System)是机器人操作系统,是一个用于编写机器人软件的程序框架。
核心概念:
节点(Node) :执行计算的基本单元
话题(Topic) :发布/订阅通信机制
服务(Service) :请求/响应通信机制
动作(Action) :长时间任务通信机制
消息(Message) :节点间传递的数据格式
参数(Parameter) :全局配置数据
主节点(Master) :管理所有节点
2. ROS 中的话题、服务、动作有什么区别? 答案:
特性
话题(Topic)
服务(Service)
动作(Action)
通信模式
发布/订阅
请求/响应
目标/反馈/结果
同步性
异步
同步
异步
方向
单向
双向
双向
关系
一对多
一对一
一对一
适用场景
流式数据
快速请求
长时间任务
示例
传感器数据
查询状态
路径规划
3. 如何创建一个 ROS 节点? 答案:
Python 节点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import rospyfrom std_msgs.msg import Stringdef node_function (): rospy.init_node('my_node' , anonymous=True ) pub = rospy.Publisher('chatter' , String, queue_size=10 ) rospy.Subscriber('chatter' , String, callback) rospy.spin() if __name__ == '__main__' : node_function()
C++ 节点:
1 2 3 4 5 6 7 8 9 10 11 12 #include <ros/ros.h> int main (int argc, char **argv) { ros::init (argc, argv, "my_node" ); ros::NodeHandle nh; ros::spin (); return 0 ; }
4. 什么是 TF?它的作用是什么? 答案: TF(Transform)是 ROS 中的坐标变换系统,用于管理机器人各个部件之间的坐标关系。
作用:
坐标变换 :在不同坐标系之间进行变换
坐标系管理 :维护坐标系之间的树形关系
时间同步 :处理不同时间戳的坐标变换
坐标查询 :查询任意两个坐标系之间的变换关系
TF 树结构:
1 2 3 4 5 map └── odom └── base_link ├── camera_link └── laser_link
5. Launch 文件的作用是什么? 答案: Launch 文件是 XML 格式的文件,用于:
启动多个节点 :一次性启动多个相关节点
设置参数 :配置节点参数
重映射话题 :改变话题名称
命名空间 :为节点设置命名空间
包含其他文件 :复用其他 launch 文件
示例:
1 2 3 4 5 6 7 <launch > <node name ="node1" pkg ="package1" type ="exec1" /> <node name ="node2" pkg ="package2" type ="exec2" > <param name ="param_name" value ="value" /> <remap from ="old_topic" to ="new_topic" /> </node > </launch >
6. ROS 消息和服务的定义有什么区别? 答案:
消息(Message)定义:
文件扩展名:.msg
用于话题和动作
单向数据流
示例:1 2 3 4 # Person.msg string first_name string last_name uint8 age
服务(Service)定义:
文件扩展名:.srv
用于服务通信
包含请求和响应两部分
示例:1 2 3 4 5 # AddTwoInts.srv int64 a int64 b --- int64 sum
7. 如何调试 ROS 程序? 答案:
日志输出 :使用 rospy.loginfo(), rospy.logwarn(), rospy.logerr()
命令行工具 :rostopic, rosservice, rosnode 等
可视化工具 :
rviz:3D 可视化
rqt_graph:计算图可视化
rqt_plot:数据绘图
rqt_console:日志查看
GDB 调试 (C++):rosrun --prefix 'gdb -ex run --args' package_name node_name
Bag 文件回放 :录制和回放话题数据
8. ROS 节点之间的通信方式有哪些? 答案:
话题(Topic) :发布/订阅,异步通信,适用于流式数据
服务(Service) :请求/响应,同步通信,适用于快速查询
动作(Action) :目标/反馈/结果,异步通信,适用于长时间任务
参数服务器(Parameter Server) :全局配置数据
TF :坐标变换数据
9. 什么是 rosbag?如何使用? 答案: rosbag 用于录制和回放 ROS 话题数据。
录制数据:
1 2 3 4 5 6 7 8 rosbag record /topic1 /topic2 rosbag record -a rosbag record -O my_bag.bag /topic1
回放数据:
1 2 3 4 5 6 7 8 rosbag play my_bag.bag rosbag play -r 2 my_bag.bag rosbag play my_bag.bag --topics /topic1
查看信息:
10. ROS 中的命名空间(namespace)是什么? 答案: 命名空间用于组织和隔离 ROS 资源,避免名称冲突。
作用:
避免冲突 :不同机器人或实例可以使用相同的话题名
组织资源 :将相关资源组织在一起
多机器人 :支持多个机器人同时运行
使用方式:
1 2 3 4 5 6 7 rosrun package_name node_name __ns:=robot1 <group ns="robot1" > <node name="node_name" pkg="package_name" type ="executable" /> </group>
私有参数:
1 2 rospy.set_param('~private_param' , 'value' )
11. 如何实现 ROS 节点的多线程? 答案:
Python 多线程:
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 import rospyimport threadingfrom std_msgs.msg import Stringdef callback1 (msg ): rospy.loginfo("Callback 1: %s" , msg.data) def callback2 (msg ): rospy.loginfo("Callback 2: %s" , msg.data) def node_function (): rospy.init_node('multi_thread_node' ) sub1 = rospy.Subscriber('topic1' , String, callback1) sub2 = rospy.Subscriber('topic2' , String, callback2) thread = threading.Thread(target=background_task) thread.daemon = True thread.start() rospy.spin() def background_task (): rate = rospy.Rate(1 ) while not rospy.is_shutdown(): rospy.loginfo("Background task" ) rate.sleep()
C++ 多线程:
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 #include <ros/ros.h> #include <thread> void background_task () { ros::Rate rate (1 ) ; while (ros::ok ()) { ROS_INFO ("Background task" ); rate.sleep (); } } int main (int argc, char **argv) { ros::init (argc, argv, "multi_thread_node" ); ros::NodeHandle nh; std::thread bg_thread (background_task) ; bg_thread.detach (); ros::spin (); return 0 ; }
12. ROS 1 和 ROS 2 的主要区别? 答案:
特性
ROS 1
ROS 2
中间件
自定义
DDS(Data Distribution Service)
实时性
不支持
支持
跨平台
Linux 为主
Windows、macOS、Linux
分布式
需要 Master
原生支持
安全性
有限
DDS 安全机制
性能
较好
更好
向后兼容
-
不完全兼容
生命周期
不支持
支持生命周期节点
13. 什么是 ROS 的 Master 节点? 答案: ROS Master 是一个中央服务器,负责:
节点注册 :管理所有节点的注册信息
话题发现 :帮助发布者和订阅者找到彼此
服务发现 :帮助客户端找到服务器
参数管理 :提供参数服务器功能
启动 Master:
Master 的作用:
节点启动时向 Master 注册
发布者和订阅者通过 Master 发现彼此
客户端通过 Master 查找服务器
不参与实际数据传输,只负责发现和注册
14. 如何处理 ROS 节点的异常? 答案:
Python 异常处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import rospydef node_function (): try : rospy.init_node('my_node' ) while not rospy.is_shutdown(): pass except rospy.ROSInterruptException: rospy.loginfo("Node interrupted" ) except Exception as e: rospy.logerr("Error: %s" , e) finally : rospy.loginfo("Node shutdown" )
C++ 异常处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <ros/ros.h> #include <exception> int main (int argc, char **argv) { try { ros::init (argc, argv, "my_node" ); ros::NodeHandle nh; ros::spin (); } catch (const std::exception& e) { ROS_ERROR ("Exception: %s" , e.what ()); return 1 ; } return 0 ; }
15. 如何优化 ROS 节点的性能? 答案:
减少消息拷贝 :使用指针和引用
批量处理 :合并多个小消息
异步处理 :避免阻塞主循环
合理设置队列大小 :避免消息丢失或积压
使用定时器 :替代轮询
减少日志输出 :生产环境减少日志
优化回调函数 :保持回调函数简短
**使用 C++**:性能关键部分使用 C++
总结 核心要点:
ROS 基础 :
理解 ROS 的分布式架构
掌握节点、话题、服务、动作等核心概念
熟悉 ROS 文件系统结构
通信机制 :
话题:发布/订阅,异步通信
服务:请求/响应,同步通信
动作:长时间任务,支持取消和反馈
工具和调试 :
rqt 工具集
rviz 可视化
rosbag 数据录制
命令行工具
高级特性 :
TF 坐标变换
Launch 文件
自定义消息和服务
URDF 机器人模型
面试重点:
ROS 核心概念(节点、话题、服务、动作)
话题、服务、动作的区别和应用场景
TF 坐标变换系统
Launch 文件编写
节点创建和调试方法
ROS 1 vs ROS 2 的区别
Master 节点的作用
异常处理和性能优化
实际应用: 在实际项目中:
选择合适的通信机制 :根据场景选择话题、服务或动作
合理设计节点 :单一职责,高内聚低耦合
使用 Launch 文件 :简化部署和启动
调试和监控 :使用 rqt 工具集进行调试
性能优化 :减少消息拷贝,优化回调函数
错误处理 :妥善处理异常,保证系统稳定性
参考资料: