引言:为什么需要这份 ROS 基础题库?
机器人操作系统(Robot Operating System, ROS)已经成为机器人开发领域的事实标准。对于初学者来说,ROS 庞大的概念体系和命令行工具往往令人望而生畏。这份精选的 100 道基础题目,旨在通过问答形式帮助你系统性地掌握 ROS 的核心概念和常用命令。
这份题库的特点:
- 针对性强:聚焦于最常用、最核心的概念和命令
- 结构清晰:按照 ROS 知识体系分门别类
- 实用导向:每道题都配有详细解释和实际应用场景
- 面试友好:覆盖了 90% 的 ROS 相关面试问题
第一部分:ROS 基础概念(1-20 题)
1. 什么是 ROS?它的主要特点是什么?
答案: ROS(Robot Operating System)是一个为机器人软件开发设计的元操作系统。它不是一个传统意义上的操作系统,而是一个运行在 Ubuntu(或其他 Linux 发行版)之上的中间件框架。
主要特点:
- 分布式架构:节点可以运行在不同的机器上,通过网络通信
- 语言无关性:支持 C++、Python、Java 等多种语言
- 丰富的工具:提供了可视化、调试、记录等大量工具
- 模块化:功能封装在包中,可以复用和共享
- 开源社区:拥有庞大的开源软件包和活跃的社区
实际应用:在自动驾驶汽车中,感知、规划、控制等不同功能可以作为独立的节点运行,通过 ROS 的通信机制协同工作。
2. ROS 的主要组件有哪些?
答案: ROS 的核心组件包括:
- 节点(Node):执行计算的基本单元
- 话题(Topic):节点间异步通信的通道
- 服务(Service):节点间同步通信的机制
- 消息(Message):节点间传输的数据结构
- 参数服务器(Parameter Server):存储配置参数的共享字典
- 节点管理器(Master):提供节点注册和查找服务
3. 什么是节点(Node)?
答案: 节点是 ROS 中执行特定功能的计算单元。一个机器人系统通常由多个节点组成,每个节点负责一个独立的任务。
示例:
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
def talker():
# 初始化节点
rospy.init_node('talker_node', anonymous=True)
# 创建发布者
pub = rospy.Publisher('chatter', String, queue_size=10)
rate = rospy.Rate(10) # 10Hz
while not rospy.is_shutdown():
msg = String()
msg.data = "Hello ROS!"
pub.publish(msg)
rate.sleep()
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
pass
4. 什么是话题(Topic)?
答案: 话题是 ROS 中节点间异步通信的机制。它采用发布-订阅模式,发布者将消息发布到话题,订阅该话题的节点会收到消息。
关键特性:
- 单向通信:数据从发布者流向订阅者
- 多对多:一个话题可以有多个发布者和订阅者
- 异步:发布者不需要等待订阅者接收
实际场景:摄像头节点发布图像话题,视觉处理节点订阅该话题进行分析。
5. 什么是服务(Service)?
答案: 服务是 ROS 中节点间同步通信的机制。它采用客户端-服务器模式,客户端发送请求,服务器处理后返回响应。
与话题的区别:
- 话题:异步、单向、多对多
- 服务:同步、双向、一对一
示例代码:
# 服务端
def handle_add_two_ints(req):
print("Returning [{} + {}] = {}".format(req.a, req.b, req.a + req.b))
return AddTwoIntsResponse(req.a + req.b)
def add_two_ints_server():
rospy.init_node('add_two_ints_server')
s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)
print("Ready to add two ints.")
rospy.spin()
# 客户端
def add_two_ints_client(x, y):
rospy.wait_for_service('add_two_ints')
try:
add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)
resp1 = add_two_ints(x, y)
return resp1.sum
except rospy.ServiceException as e:
print("Service call failed: %s" % e)
6. 什么是消息(Message)?
答案: 消息是 ROS 中节点间传输的数据结构。它定义了通信的数据格式,类似于编程语言中的结构体。
常见消息类型:
std_msgs/String:字符串geometry_msgs/Twist:速度指令sensor_msgs/Image:图像数据nav_msgs/Odometry:里程计信息
自定义消息示例:
# 在 msg/Person.msg 文件中
string name
int32 age
float32 height
7. 什么是参数服务器(Parameter Server)?
答案: 参数服务器是一个共享的字典,节点可以通过它存储和获取配置参数。它支持动态配置,无需重新编译代码。
操作方式:
# 设置参数
rospy.set_param('/my_param', 'value')
# 获取参数
value = rospy.get_param('/my_param', 'default_value')
# 删除参数
rospy.delete_param('/my_param')
# 检查参数是否存在
if rospy.has_param('/my_param'):
print("Parameter exists")
8. 什么是节点管理器(Master)?
答案: 节点管理器是 ROS 系统的”电话簿”,负责节点的注册、查找和协调。它提供了命名服务,让节点能够相互发现。
重要提示:
- Master 必须在所有节点启动前运行
- 使用
roscore命令启动 - 如果 Master 宕机,新节点无法加入系统
9. 什么是包(Package)?
答案: 包是 ROS 中组织软件的基本单元。一个包可以包含节点、库、配置文件、消息定义等。
包的结构:
my_package/
├── CMakeLists.txt
├── package.xml
├── src/
├── include/
├── msg/
├── srv/
└── launch/
10. 如何创建一个 ROS 包?
答案:
使用 catkin_create_pkg 命令创建包:
# 语法
catkin_create_pkg <package_name> [depend1] [depend2] ...
# 示例
catkin_create_pkg my_ros_package rospy roscpp std_msgs geometry_msgs
创建后的步骤:
- 编写代码(节点)
- 修改 CMakeLists.txt
- 编译:
catkin_make - source 环境:
source devel/setup.bash
11. 什么是工作空间(Workspace)?
答案: 工作空间是存放 ROS 包的目录结构,包含编译、安装等过程的中间文件。
标准结构:
catkin_ws/
├── src/ # 源代码空间
├── build/ # 编译空间
├── devel/ # 开发空间
└── install/ # 安装空间(可选)
12. 如何初始化工作空间?
答案:
# 创建目录结构
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
# 初始化工作空间
catkin_init_workspace
# 回到工作空间根目录编译
cd ~/catkin_ws
catkin_make
13. 什么是 launch 文件?
答案: Launch 文件是 XML 格式的文件,用于一次性启动多个节点和设置参数。它简化了复杂系统的启动过程。
示例:
<launch>
<!-- 启动两个节点 -->
<node pkg="my_package" type="talker.py" name="talker" output="screen"/>
<node pkg="my_package" type="listener.py" name="listener" output="screen"/>
<!-- 设置参数 -->
<param name="rate" type="int" value="10"/>
<!-- 包含其他 launch 文件 -->
<include file="$(find another_package)/launch/another.launch"/>
</launch>
14. 如何运行 launch 文件?
答案:
# 语法
roslaunch <package_name> <launch_file>
# 示例
roslaunch my_package my_launch.launch
# 也可以直接指定完整路径
roslaunch /path/to/launch/file.launch
15. 什么是命名空间(Namespace)?
答案: 命名空间用于避免节点、话题、参数的命名冲突。它为相关组件提供了一个逻辑分组。
示例:
<launch>
<group ns="robot1">
<node pkg="my_package" type="node.py" name="my_node"/>
</group>
<group ns="robot2">
<node pkg="my_package" type="node.py" name="my_node"/>
</group>
</launch>
这样,两个同名的节点分别位于 /robot1/my_node 和 /robot2/my_node。
16. 什么是全局名称、相对名称和私有名称?
答案:
- 全局名称:以
/开头,如/sensor/data - 相对名称:不以
/开头,如data,会根据当前命名空间解析 - 私有名称:以
~开头,用于节点私有参数,如~config
示例:
# 在节点 /robot/sensors/imu 中
rospy.get_param('~rate') # 访问 /robot/sensors/imu/rate
rospy.get_param('rate') # 访问 /robot/sensors/rate
rospy.get_param('/rate') # 访问 /rate
17. 什么是动作(Action)?
答案: 动作是 ROS 中用于长时间运行任务的通信机制。它包含目标、反馈和结果三部分。
应用场景:导航、机械臂运动等需要持续执行并反馈进度的任务。
示例:
# 动作客户端
client = actionlib.SimpleActionClient('move_base', MoveBaseAction)
client.wait_for_server()
goal = MoveBaseGoal()
client.send_goal(goal)
client.wait_for_result()
18. 什么是 TF(坐标变换)?
答案: TF 是 ROS 中用于管理坐标系之间变换关系的系统。它维护了机器人各部件之间的位置和姿态关系。
核心概念:
- 坐标系(Frame):如
base_link、laser、map - 变换(Transform):两个坐标系之间的相对关系
- TF 树:存储所有变换关系的数据结构
19. 什么是 URDF?
答案: URDF(Unified Robot Description Format)是 ROS 中用于描述机器人模型的 XML 格式文件。它定义了机器人的连杆、关节、视觉和碰撞属性。
示例:
<?xml version="1.0"?>
<robot name="my_robot">
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.2 0.1"/>
</geometry>
</visual>
</link>
<link name="wheel_left">
<visual>
<geometry>
<cylinder radius="0.05" length="0.02"/>
</geometry>
</visual>
</link>
<joint name="base_to_wheel_left" type="continuous">
<parent link="base_link"/>
<child link="wheel_left"/>
<origin xyz="0.1 0.1 0" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
</joint>
</robot>
20. 什么是 Rviz?
答案: Rviz 是 ROS 的 3D 可视化工具,用于显示机器人模型、传感器数据、规划路径等。
常用功能:
- 显示 URDF 机器人模型
- 可视化点云、激光扫描等传感器数据
- 显示 TF 坐标系
- 显示导航路径和障碍物
启动命令:
rviz
# 或指定配置文件
rviz -d config.rviz
第二部分:ROS 常用命令(21-50 题)
21. 如何启动 ROS Master?
答案:
roscore
这会启动:
- ROS Master
- ROS 参数服务器
- ROS 日志系统
指定端口:
roscore -p 11311
22. 如何查看运行中的节点?
答案:
# 查看所有运行中的节点
rosnode list
# 查看节点的详细信息
rosnode info /node_name
# 查看节点的通信连接
rosnode ping /node_name
23. 如何查看所有话题?
答案:
# 查看所有话题
rostopic list
# 查看话题的详细信息
rostopic info /topic_name
# 查看话题的数据类型
rostopic type /topic_name
# 查看话题的数据内容
rostopic echo /topic_name
# 查看话题的发布频率
rostopic hz /topic_name
24. 如何发布一个话题消息?
答案:
# 发布字符串消息
rostopic pub /chatter std_msgs/String "Hello ROS"
# 以 1Hz 的频率持续发布
rostopic pub -r 1 /chatter std_msgs/String "Hello ROS"
# 发布几何速度指令
rostopic pub /cmd_vel geometry_msgs/Twist "linear:
x: 0.5
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.2"
25. 如何查看和设置参数?
答案:
# 查看所有参数
rosparam list
# 获取参数值
rosparam get /param_name
# 设置参数值
rosparam set /param_name value
# 将参数保存到 YAML 文件
rosparam dump params.yaml
# 从 YAML 文件加载参数
rosparam load params.yaml
26. 如何调用服务?
答案:
# 调用服务(需要知道服务类型)
rosservice call /service_name "args"
# 示例:调用 add_two_ints 服务
rosservice call /add_two_ints "a: 1
b: 2"
# 查看服务列表
rosservice list
# 查看服务类型
rosservice type /service_name
# 查看服务参数结构
rosservice args /service_name
27. 如何查看和记录数据包?
答案:
# 查看所有数据包
rosmsg list
# 查看消息定义
rosmsg show geometry_msgs/Twist
# 查看消息使用情况
rosmsg show -r geometry_msgs/Twist
# 查看消息包的依赖
rosmsg packages geometry_msgs/Twist
28. 如何记录和回放 ROS 数据?
答案:
# 记录所有话题数据到 bag 文件
rosbag record -a
# 记录指定话题
rosbag record /topic1 /topic2
# 记录到指定文件
rosbag record -O my_bag.bag /topic1 /topic2
# 回放 bag 文件
rosbag play my_bag.bag
# 回放并以 2 倍速播放
rosbag play -r 2 my_bag.bag
# 查看 bag 文件信息
rosbag info my_bag.bag
29. 如何清理 ROS 环境?
答案:
# 清理所有 ROS 相关的临时文件和缓存
roscore clean
# 手动删除临时文件
rm -rf ~/.ros/log/*
rm -rf /tmp/ros-*
30. 如何查找 ROS 包的位置?
答案:
# 查找包的路径
rospack find package_name
# 查看包的依赖
rospack depends1 package_name
rospack depends package_name
# 查看所有包
rospack list
31. 如何查看节点的通信图?
答案:
# 生成通信图(需要安装 rqt_graph)
rqt_graph
# 或者使用命令行工具
rosnode info /node_name
32. 如何查看 TF 变换?
答案:
# 查看所有坐标系
rosrun tf view_frames
# 查看两个坐标系之间的变换
rosrun tf tf_echo /map /base_link
# 实时监控 TF 变换
rosrun tf tf_monitor /base_link /laser
33. 如何启动可视化工具?
答案:
# 启动 Rviz
rviz
# 启动 Rqt
rqt
# 启动 Rqt 插件
rqt_graph
rqt_plot
rqt_console
34. 如何查看 ROS 网络配置?
答案:
# 查看 ROS 网络配置
echo $ROS_MASTER_URI
echo $ROS_IP
echo $ROS_HOSTNAME
# 设置环境变量
export ROS_MASTER_URI=http://192.168.1.100:11311
export ROS_IP=192.168.1.50
35. 如何在不同机器间配置 ROS?
答案: 主机(Master):
export ROS_MASTER_URI=http://192.168.1.100:11311
export ROS_IP=192.168.1.100
roscore
从机(Client):
export ROS_MASTER_URI=http://192.168.1.100:11311
export ROS_IP=192.168.1.50
rosrun my_package my_node
36. 如何查看 ROS 日志?
答案:
# 查看实时日志
rosout
# 查看日志文件
roslaunch --ros-log-dir=/path/to/log
# 设置日志级别
export ROS_LOG_LEVEL=DEBUG
37. 如何调试节点?
答案:
# 使用 GDB 调试 C++ 节点
rosrun --prefix 'gdb --args' my_package my_node
# 使用 pdb 调试 Python 节点
rosrun --prefix 'python -m pdb' my_package my_node.py
# 在节点中打印调试信息
rospy.logdebug("Debug message")
rospy.loginfo("Info message")
rospy.logwarn("Warning message")
rospy.logerror("Error message")
rospy.logfatal("Fatal message")
38. 如何查看包的文件结构?
答案:
# 查看包的目录树
rospack find package_name
cd $(rospack find package_name)
tree
# 或者使用 ls
ls -R $(rospack find package_name)
39. 如何更新 ROS 系统?
答案:
# 更新软件包列表
sudo apt-get update
# 升级所有 ROS 相关包
sudo apt-get upgrade ros-<distro>-*
# 完整升级系统
sudo apt-get dist-upgrade
40. 如何卸载 ROS 包?
答案:
# 卸载单个包
sudo apt-get remove ros-<distro>-package-name
# 卸载所有 ROS 包
sudo apt-get remove ros-<distro>-*
sudo apt-get autoremove
41. 如何查看 ROS 版本?
答案:
# 查看 ROS 发行版
echo $ROS_DISTRO
# 查看 ROS 核心版本
rosversion -d
rosversion roscpp
42. 如何创建自定义消息?
答案:
- 在包的 msg 目录下创建
.msg文件 - 修改
package.xml添加依赖:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
- 修改
CMakeLists.txt:
find_package(catkin REQUIRED COMPONENTS
std_msgs
message_generation
)
add_message_files(
FILES
Person.msg
)
generate_messages(
DEPENDENCIES
std_msgs
)
catkin_package(
CATKIN_DEPENDS message_runtime std_msgs
)
- 编译包
43. 如何创建自定义服务?
答案:
- 在包的 srv 目录下创建
.srv文件 - 修改
package.xml(同消息) - 修改
CMakeLists.txt:
add_service_files(
FILES
AddTwoInts.srv
)
- 编译包
44. 如何查看节点的发布和订阅关系?
答案:
# 查看节点的发布者
rostopic info /topic_name
# 查看节点的订阅者
rostopic info /topic_name
# 使用 rqt_graph 可视化
rqt_graph
45. 如何查看系统资源使用情况?
答案:
# 查看节点的 CPU 和内存使用
rosnode info /node_name
# 使用系统工具
top | grep ros
htop | grep ros
# 查看进程 ID
ps aux | grep ros
46. 如何设置 ROS 环境变量?
答案:
# 临时设置(当前终端有效)
export ROS_MASTER_URI=http://localhost:11311
export ROS_IP=127.0.0.1
# 永久设置(添加到 ~/.bashrc)
echo "export ROS_MASTER_URI=http://localhost:11311" >> ~/.bashrc
echo "export ROS_IP=127.0.0.1" >> ~/.bashrc
source ~/.bashrc
# 使用 setup.bash 自动设置
source /opt/ros/<distro>/setup.bash
source ~/catkin_ws/devel/setup.bash
47. 如何查看 ROS 网络通信状态?
答案:
# 查看节点的连接状态
rosnode ping /node_name
# 查看所有节点的连接
rosnode list
for node in $(rosnode list); do echo "Pinging $node"; rosnode ping $node; done
# 查看网络端口
netstat -tulpn | grep 11311
48. 如何批量重命名节点?
答案:
# 使用 remap 参数
rosrun my_package my_node __name:=new_name
# 在 launch 文件中
<node pkg="my_package" type="my_node" name="new_name"/>
49. 如何查看包的编译依赖?
答案:
# 查看编译依赖
rospack depends1 package_name
# 查看所有依赖(包括间接依赖)
rospack depends package_name
# 查看运行时依赖
rospack depends1 -e package_name
50. 如何清理工作空间?
答案:
# 删除编译产物
cd ~/catkin_ws
rm -rf build devel install
# 重新编译
catkin_make
第三部分:ROS 进阶概念(51-70 题)
51. 什么是 ROS 动作库(Actionlib)?
答案: Actionlib 是 ROS 中用于实现长时间运行任务的库。它提供了目标、反馈和结果的完整框架。
核心组件:
- ActionServer:执行任务的服务器
- ActionClient:发送目标的客户端
- ActionGoal:目标消息
- ActionResult:结果消息
- ActionFeedback:反馈消息
52. 如何实现一个动作服务器?
答案:
#!/usr/bin/env python
import rospy
import actionlib
from my_package.msg import FibonacciAction, FibonacciGoal, FibonacciResult
class FibonacciServer:
def __init__(self):
self.server = actionlib.SimpleActionServer('fibonacci', FibonacciAction, self.execute, False)
self.server.start()
def execute(self, goal):
rate = rospy.Rate(1)
sequence = [0, 1]
for i in range(1, goal.order):
if self.server.is_preempt_requested():
self.server.set_preempted()
return
sequence.append(sequence[i] + sequence[i-1])
feedback = FibonacciFeedback()
feedback.sequence = sequence
self.server.publish_feedback(feedback)
rate.sleep()
result = FibonacciResult()
result.sequence = sequence
self.server.set_succeeded(result)
if __name__ == '__main__':
rospy.init_node('fibonacci_server')
server = FibonacciServer()
rospy.spin()
53. 如何实现一个动作客户端?
答案:
#!/usr/bin/env python
import rospy
import actionlib
from my_package.msg import FibonacciAction, FibonacciGoal
def fibonacci_client():
client = actionlib.SimpleActionClient('fibonacci', FibonacciAction)
client.wait_for_server()
goal = FibonacciGoal(order=10)
client.send_goal(goal)
client.wait_for_result()
return client.get_result()
if __name__ == '__main__':
try:
result = fibonacci_client()
print("Result:", result.sequence)
except rospy.ROSInterruptException:
print("Program interrupted")
54. 什么是 ROS 插件(Plugin)?
答案: ROS 插件允许在运行时动态加载功能模块,而无需重新编译主程序。常用于 Rviz 插件、导航插件等。
实现步骤:
- 定义插件基类
- 实现具体插件类
- 注册插件
- 在 XML 文件中声明插件
55. 什么是 ROS 动态配置(Dynamic Reconfigure)?
答案: 动态配置允许在运行时修改节点的参数,无需重启节点。常用于调整算法参数。
实现步骤:
- 创建 cfg 文件
- 编译生成配置代码
- 在节点中集成
示例 cfg 文件:
#!/usr/bin/env python
PACKAGE = "my_package"
from dynamic_reconfigure.parameter_generator_catkin import *
def generate():
gen = ParameterGenerator()
gen.add("double_param", double_t, 0, "A double parameter", 0.5, 0, 1)
gen.add("int_param", int_t, 0, "An integer parameter", 1, 0, 10)
gen.add("str_param", str_t, 0, "A string parameter", "Hello")
return gen.generate(PACKAGE, "my_package", "MyConfig")
56. 什么是 ROS 节点let(Nodelet)?
答案: Nodelet 用于将多个节点合并到一个进程中运行,减少进程间通信的开销。它在同一个进程中运行多个”节点”,通过共享内存传递数据。
优势:
- 减少数据复制
- 降低延迟
- 提高效率
应用场景:图像处理管道、传感器数据融合。
57. 什么是 ROS 网络(ROS Network)?
答案: ROS 网络是指通过 TCP/IP 协议连接的多个 ROS 节点组成的分布式系统。关键配置包括:
ROS_MASTER_URI:Master 的地址ROS_IP或ROS_HOSTNAME:节点的 IP 地址
58. 如何实现多机通信?
答案: 主机(Machine A):
export ROS_MASTER_URI=http://192.168.1.100:11311
export ROS_IP=192.168.1.100
roscore
rosrun my_package talker.py
从机(Machine B):
export ROS_MASTER_URI=http://192.168.1.100:11311
export ROS_IP=192.168.1.50
rosrun my_package listener.py
59. 什么是 ROS 测试框架?
答案: ROS 提供了基于 rostest 的测试框架,支持单元测试和集成测试。
测试类型:
- rospy / roscpp:单元测试
- rostest:集成测试
- rosbag:基于记录的测试
60. 如何编写 ROS 测试?
答案:
#!/usr/bin/env python
import unittest
import rospy
from std_msgs.msg import String
class TestTalker(unittest.TestCase):
def test_talker(self):
received = []
def callback(msg):
received.append(msg.data)
sub = rospy.Subscriber('chatter', String, callback)
rospy.sleep(1) # 等待消息
self.assertTrue(len(received) > 0)
self.assertEqual(received[0], "Hello ROS!")
if __name__ == '__main__':
rospy.init_node('test_talker')
unittest.main()
61. 什么是 ROS 诊断(Diagnostics)?
答案: ROS 诊断系统用于监控和报告机器人系统的状态。它提供了标准化的方式收集和显示诊断信息。
核心组件:
- diagnostic_updater:发布诊断信息
- diagnostic_aggregator:聚合诊断信息
- rqt_robot_monitor:显示诊断信息
62. 如何使用诊断工具?
答案:
#!/usr/bin/env python
import rospy
from diagnostic_updater import Updater, DiagnosticTask
from diagnostic_msgs.msg import DiagnosticStatus
class MyDiagnosticTask(DiagnosticTask):
def __init__(self):
super(MyDiagnosticTask, self).__init__("My Task")
def run(self, stat):
# 检查逻辑
if self.check_ok():
stat.summary(DiagnosticStatus.OK, "All good")
else:
stat.summary(DiagnosticStatus.ERROR, "Something wrong")
stat.add("Value", str(self.get_value()))
return stat
updater = Updater()
updater.add(MyDiagnosticTask())
rospy.Timer(rospy.Duration(1.0), lambda event: updater.update())
63. 什么是 ROS 时间(Time)?
答案: ROS 提供了统一的时间管理机制,支持仿真时间和真实时间。
关键概念:
- rospy.Time:时间戳
- rospy.Rate:频率控制
- rospy.Duration:时间间隔
- rospy.Time.now():当前时间
64. 如何处理仿真时间?
答案:
# 启动仿真时间
rosparam set /use_sim_time true
# 使用 gazebo 等仿真器发布 /clock 话题
代码中处理:
# 等待仿真时间开始
rospy.wait_for_message('/clock', rospy.Time)
# 获取当前时间(仿真或真实)
now = rospy.Time.now()
65. 什么是 ROS 日志系统?
答案: ROS 日志系统提供了分级的日志记录功能,支持不同的日志级别和输出格式。
日志级别:
- DEBUG
- INFO
- WARN
- ERROR
- FATAL
66. 如何配置 ROS 日志?
答案:
# 设置日志级别
export ROS_LOG_LEVEL=DEBUG
# 配置日志文件
export ROS_LOG_DIR=/path/to/logs
# 在代码中使用
rospy.loginfo("This is an info message")
rospy.logwarn("This is a warning")
67. 什么是 ROS 启动文件(Launch File)的高级用法?
答案: Launch 文件支持条件执行、参数替换、环境变量等高级功能。
示例:
<launch>
<!-- 条件执行 -->
<group if="$(arg use_sim)">
<node pkg="gazebo_ros" type="spawn_model.py" name="spawn"/>
</group>
<!-- 参数替换 -->
<param name="rate" value="$(arg rate)"/>
<!-- 环境变量 -->
<env name="MY_VAR" value="my_value"/>
<!-- 重命名话题 -->
<remap from="input" to="output"/>
</launch>
68. 如何调试 launch 文件?
答案:
# 显示 launch 文件的解析结果
roslaunch --debug my_package my_launch.launch
# 使用 verbose 模式
roslaunch -v my_package my_launch.launch
# 检查语法
xmllint --noout my_launch.launch
69. 什么是 ROS 包管理工具?
答案: ROS 提供了多种包管理工具:
- rosdep:安装依赖
- rosinstall:管理源码
- vcstool:版本控制工具
70. 如何使用 rosdep 安装依赖?
答案:
# 安装包的所有依赖
rosdep install --from-paths src --ignore-src -y
# 安装特定包的依赖
rosdep install package_name
# 更新 rosdep 数据库
rosdep update
第四部分:ROS 实际应用与面试技巧(71-100 题)
71. 如何实现一个完整的机器人控制系统?
答案: 一个典型的机器人控制系统包含以下节点:
- 传感器节点:读取传感器数据
- 感知节点:处理传感器数据
- 规划节点:路径规划
- 控制节点:发送控制指令
- 执行节点:驱动电机
架构示例:
# 传感器节点
sensor_node -> /sensor_data (SensorMsg)
# 感知节点
感知节点订阅 /sensor_data -> /obstacles (ObstacleArray)
# 规划节点
规划节点订阅 /obstacles -> /plan (Path)
# 控制节点
控制节点订阅 /plan -> /cmd_vel (Twist)
# 执行节点
执行节点订阅 /cmd_vel -> 电机驱动
72. 如何处理传感器数据的同步?
答案:
使用 message_filters 进行时间同步:
#!/usr/bin/env python
import rospy
import message_filters
from sensor_msgs.msg import Image, LaserScan
def callback(image, scan):
# 同步处理图像和激光数据
print("Received synchronized data")
rospy.init_node('sensor_fusion')
image_sub = message_filters.Subscriber('/camera/image', Image)
scan_sub = message_filters.Subscriber('/scan', LaserScan)
# 时间同步器
ts = message_filters.ApproximateTimeSynchronizer(
[image_sub, scan_sub], queue_size=10, slop=0.1
)
ts.registerCallback(callback)
rospy.spin()
73. 如何实现路径规划?
答案: 使用 ROS 的 navigation 堆栈:
#!/usr/bin/env python
import rospy
import actionlib
from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal
def move_to_goal(x, y, theta):
client = actionlib.SimpleActionClient('move_base', MoveBaseAction)
client.wait_for_server()
goal = MoveBaseGoal()
goal.target_pose.header.frame_id = "map"
goal.target_pose.header.stamp = rospy.Time.now()
goal.target_pose.pose.position.x = x
goal.target_pose.pose.position.y = y
goal.target_pose.pose.orientation.w = theta
client.send_goal(goal)
client.wait_for_result()
return client.get_state() == actionlib.GoalStatus.SUCCEEDED
if __name__ == '__main__':
rospy.init_node('navigation_client')
success = move_to_goal(1.0, 2.0, 1.0)
print("Navigation success:", success)
74. 如何实现 SLAM?
答案: 使用 ROS 的 SLAM 工具包:
# 启动 Gazebo 仿真
roslaunch turtlebot3_gazebo turtlebot3_world.launch
# 启动 SLAM
roslaunch turtlebot3_slam turtlebot3_slam.launch
# 启动导航
roslaunch turtlebot3_navigation turtlebot3_navigation.launch
代码实现:
# 发送速度指令进行建图
rostopic pub /cmd_vel geometry_msgs/Twist "linear:
x: 0.2
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.3"
75. 如何实现物体抓取?
答案:
#!/usr/bin/env python
import rospy
from moveit_commander import MoveGroupCommander
def pick_object():
# 初始化 MoveIt
group = MoveGroupCommander("arm")
# 设置目标姿态
pose_target = Pose()
pose_target.position.x = 0.3
pose_target.position.y = 0.0
pose_target.position.z = 0.2
pose_target.orientation.w = 1.0
group.set_pose_target(pose_target)
plan = group.go(wait=True)
# 执行抓取
group.attach_object("object_name")
# 移动到目标位置
group.set_named_target("home")
group.go(wait=True)
if __name__ == '__main__':
rospy.init_node('grasp_node')
pick_object()
76. 如何实现多机器人协作?
答案:
# 机器人 1
rospy.init_node('robot1_controller')
pub1 = rospy.Publisher('/robot1/cmd_vel', Twist, queue_size=10)
sub1 = rospy.Subscriber('/robot2/status', Status, robot2_callback)
# 机器人 2
rospy.init_node('robot2_controller')
pub2 = rospy.Publisher('/robot2/cmd_vel', Twist, queue_size=10)
sub2 = rospy.Subscriber('/robot1/status', Status, robot1_callback)
# 使用命名空间避免冲突
# 在 launch 文件中使用 <group ns="robot1"> 和 <group ns="robot2">
77. 如何优化 ROS 系统性能?
答案:
- 使用 Nodelet:减少进程间通信开销
- 调整队列大小:避免消息丢失
- 使用异步回调:避免阻塞
- 优化消息类型:减少数据大小
- 使用共享内存:对于大数据传输
示例:
# 使用 Nodelet(C++)
#include <nodelet/nodelet.h>
#include <pluginlib/class_list_macros.h>
namespace my_namespace {
class MyNodelet : public nodelet::Nodelet {
virtual void onInit() {
// 初始化代码
}
};
}
PLUGINLIB_EXPORT_CLASS(my_namespace::MyNodelet, nodelet::Nodelet)
78. 如何处理 ROS 系统故障?
答案:
# 使用 try-catch 处理异常
try:
# 可能失败的操作
result = some_service_call()
except rospy.ServiceException as e:
rospy.logerr("Service call failed: %s", e)
# 重试或降级处理
result = fallback_solution()
# 使用心跳机制监控节点
def heartbeat():
while not rospy.is_shutdown():
# 发布心跳
pub.publish(Heartbeat())
rospy.sleep(1.0)
# 使用诊断系统
updater = Updater()
updater.add("Node Health", check_health)
79. 如何实现 ROS 安全机制?
答案:
# 1. 使用命名空间隔离
# 2. 设置权限
# 3. 使用认证(ROS 2 支持更好)
# 示例:检查消息来源
def validate_message(msg, expected_source):
if msg.header.frame_id != expected_source:
rospy.logwarn("Received message from unexpected source")
return False
return True
# 使用参数服务器存储安全配置
secret_key = rospy.get_param('~secret_key', 'default')
80. 如何调试复杂的 ROS 系统?
答案:
# 1. 使用 rqt_graph 查看节点关系
rqt_graph
# 2. 使用 rqt_console 查看日志
rqt_console
# 3. 使用 rostopic 查看数据流
rostopic echo /topic_name
rostopic hz /topic_name
# 4. 使用 rosbag 记录和回放
rosbag record -a
rosbag play my_bag.bag
# 5. 使用 gdb 调试
gdb --args rosrun my_package my_node
81. 如何实现 ROS 参数动态调整?
答案:
#!/usr/bin/env python
import rospy
from dynamic_reconfigure.server import Server
from my_package.cfg import MyConfigConfig
def callback(config, level):
rospy.loginfo("Reconfigure Request: {double_param}, {int_param}, {str_param}".format(**config))
# 应用新参数
global my_param
my_param = config['double_param']
return config
rospy.init_node('dynamic_reconfigure_node')
server = Server(MyConfigConfig, callback)
rospy.spin()
82. 如何实现 ROS 数据记录与分析?
答案:
#!/usr/bin/env python
import rospy
import rosbag
from sensor_msgs.msg import Image
class DataLogger:
def __init__(self, bag_name):
self.bag = rosbag.Bag(bag_name, 'w')
self.sub = rospy.Subscriber('/camera/image', Image, self.callback)
def callback(self, msg):
self.bag.write('/camera/image', msg, msg.header.stamp)
def close(self):
self.bag.close()
if __name__ == '__main__':
rospy.init_node('data_logger')
logger = DataLogger('my_data.bag')
rospy.spin()
logger.close()
83. 如何实现 ROS 可视化?
答案:
#!/usr/bin/env python
import rospy
from visualization_msgs.msg import Marker
def publish_marker():
pub = rospy.Publisher('/visualization_marker', Marker, queue_size=10)
rospy.sleep(1) # 等待订阅者
marker = Marker()
marker.header.frame_id = "base_link"
marker.header.stamp = rospy.Time.now()
marker.ns = "my_namespace"
marker.id = 0
marker.type = Marker.SPHERE
marker.action = Marker.ADD
marker.pose.position.x = 1.0
marker.pose.position.y = 0.5
marker.pose.position.z = 0.0
marker.scale.x = 0.1
marker.scale.y = 0.1
marker.scale.z = 0.1
marker.color.a = 1.0
marker.color.r = 1.0
marker.color.g = 0.0
marker.color.b = 0.0
pub.publish(marker)
if __name__ == '__main__':
rospy.init_node('marker_publisher')
rate = rospy.Rate(10)
while not rospy.is_shutdown():
publish_marker()
rate.sleep()
84. 如何实现 ROS 与外部系统集成?
答案:
#!/usr/bin/env python
import rospy
import socket
import json
from std_msgs.msg import String
class ROSBridge:
def __init__(self):
self.pub = rospy.Publisher('/external_data', String, queue_size=10)
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.bind(('localhost', 8888))
def run(self):
while not rospy.is_shutdown():
data, addr = self.sock.recvfrom(1024)
msg = String()
msg.data = data.decode('utf-8')
self.pub.publish(msg)
if __name__ == '__main__':
rospy.init_node('ros_bridge')
bridge = ROSBridge()
bridge.run()
85. 如何实现 ROS 系统监控?
答案:
#!/usr/bin/env python
import rospy
import psutil
from diagnostic_msgs.msg import DiagnosticArray, DiagnosticStatus
class SystemMonitor:
def __init__(self):
self.pub = rospy.Publisher('/diagnostics', DiagnosticArray, queue_size=10)
self.timer = rospy.Timer(rospy.Duration(1.0), self.publish_diagnostics)
def publish_diagnostics(self, event):
diag = DiagnosticArray()
diag.header.stamp = rospy.Time.now()
# CPU 使用率
cpu_status = DiagnosticStatus()
cpu_status.name = "CPU Usage"
cpu_status.level = DiagnosticStatus.OK
cpu_status.message = "Normal"
cpu_status.values.append(
DiagnosticStatus.Value(key="Usage", value=str(psutil.cpu_percent()))
)
diag.status.append(cpu_status)
# 内存使用率
mem_status = DiagnosticStatus()
mem_status.name = "Memory Usage"
mem_status.level = DiagnosticStatus.OK
mem_status.message = "Normal"
mem_status.values.append(
DiagnosticStatus.Value(key="Usage", value=str(psutil.virtual_memory().percent))
)
diag.status.append(mem_status)
self.pub.publish(diag)
if __name__ == '__main__':
rospy.init_node('system_monitor')
monitor = SystemMonitor()
rospy.spin()
86. 如何实现 ROS 与深度学习框架集成?
答案:
#!/usr/bin/env python
import rospy
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import cv2
import numpy as np
class DeepLearningNode:
def __init__(self):
self.bridge = CvBridge()
self.sub = rospy.Subscriber('/camera/image', Image, self.image_callback)
self.pub = rospy.Publisher('/detection_result', Detection2DArray, queue_size=10)
# 加载模型(示例使用 OpenCV DNN)
self.net = cv2.dnn.readNetFromTensorflow('model.pb', 'config.pbtxt')
def image_callback(self, msg):
# 转换为 OpenCV 图像
cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")
# 深度学习推理
blob = cv2.dnn.blobFromImage(cv_image, size=(300, 300), swapRB=True, crop=False)
self.net.setInput(blob)
detections = self.net.forward()
# 发布结果
result_msg = Detection2DArray()
# ... 填充消息
self.pub.publish(result_msg)
if __name__ == '__main__':
rospy.init_node('dl_node')
node = DeepLearningNode()
rospy.spin()
87. 如何实现 ROS 与数据库集成?
答案:
#!/usr/bin/env python
import rospy
import sqlite3
from std_msgs.msg import String
class DatabaseNode:
def __init__(self):
self.conn = sqlite3.connect('/path/to/robot_data.db')
self.create_table()
self.sub = rospy.Subscriber('/sensor_data', String, self.store_data)
def create_table(self):
cursor = self.conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS sensor_log (
id INTEGER PRIMARY KEY,
timestamp REAL,
data TEXT
)
''')
self.conn.commit()
def store_data(self, msg):
cursor = self.conn.cursor()
cursor.execute('''
INSERT INTO sensor_log (timestamp, data)
VALUES (?, ?)
''', (rospy.Time.now().to_sec(), msg.data))
self.conn.commit()
if __name__ == '__main__':
rospy.init_node('database_node')
node = DatabaseNode()
rospy.spin()
node.conn.close()
88. 如何实现 ROS 与 Web 前端集成?
答案:
#!/usr/bin/env python
import rospy
from flask import Flask, jsonify
from std_msgs.msg import String
import threading
app = Flask(__name__)
latest_data = None
def ros_subscriber():
global latest_data
rospy.Subscriber('/sensor_data', String, lambda msg: setattr(latest_data, 'data', msg.data))
@app.route('/api/data')
def get_data():
return jsonify({'data': latest_data})
def start_flask():
app.run(host='0.0.0.0', port=5000)
if __name__ == '__main__':
rospy.init_node('web_bridge')
# 启动 Flask 在独立线程
flask_thread = threading.Thread(target=start_flask)
flask_thread.daemon = True
flask_thread.start()
# ROS 循环
rospy.spin()
89. 如何实现 ROS 与 PLC/工业设备通信?
答案:
#!/usr/bin/env python
import rospy
from modbus_tk import modbus_tcp
from std_msgs.msg import Float32
class PLCBridge:
def __init__(self):
self.master = modbus_tcp.TcpMaster(host='192.168.1.100', port=502)
self.pub = rospy.Publisher('/plc_data', Float32, queue_size=10)
self.timer = rospy.Timer(rospy.Duration(0.1), self.read_plc)
def read_plc(self, event):
try:
# 读取寄存器
result = self.master.execute(slave=1, function_code=3, starting_address=0, quantity_of_x=1)
data = Float32()
data.data = result[0]
self.pub.publish(data)
except Exception as e:
rospy.logerr("PLC communication error: %s", e)
if __name__ == '__main__':
rospy.init_node('plc_bridge')
bridge = PLCBridge()
rospy.spin()
90. 如何实现 ROS 与云平台集成?
答案:
#!/usr/bin/env python
import rospy
import boto3
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
class CloudBridge:
def __init__(self):
self.s3 = boto3.client('s3')
self.bridge = CvBridge()
self.sub = rospy.Subscriber('/camera/image', Image, self.upload_to_cloud)
def upload_to_cloud(self, msg):
cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")
_, img_encoded = cv2.imencode('.jpg', cv_image)
# 上传到 S3
timestamp = rospy.Time.now().to_nsec()
self.s3.put_object(
Bucket='my-robot-bucket',
Key=f'images/{timestamp}.jpg',
Body=img_encoded.tobytes()
)
if __name__ == '__main__':
rospy.init_node('cloud_bridge')
bridge = CloudBridge()
rospy.spin()
91. 如何实现 ROS 系统的自动化部署?
答案:
#!/bin/bash
# deploy.sh
# 1. 更新系统
sudo apt-get update
# 2. 安装 ROS
sudo apt-get install ros-<distro>-desktop-full
# 3. 创建工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
# 4. 克隆代码
git clone https://github.com/your/repo.git
# 5. 安装依赖
cd ~/catkin_ws
rosdep install --from-paths src --ignore-src -y
# 6. 编译
catkin_make
# 7. 设置环境
echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc
# 8. 启动系统
roslaunch my_package system.launch
92. 如何实现 ROS 系统的持续集成?
答案:
# .github/workflows/ros.yml
name: ROS CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup ROS
uses: ros-tooling/setup-ros@v0.2
with:
required-ros-distributions: noetic
- name: Install dependencies
run: |
sudo apt-get update
rosdep install --from-paths src --ignore-src -y
- name: Build
run: |
source /opt/ros/noetic/setup.bash
catkin_make
- name: Test
run: |
source devel/setup.bash
catkin_make run_tests
93. 如何实现 ROS 系统的监控告警?
答案:
#!/usr/bin/env python
import rospy
import smtplib
from email.mime.text import MIMEText
from diagnostic_msgs.msg import DiagnosticArray
class AlertSystem:
def __init__(self):
self.sub = rospy.Subscriber('/diagnostics', DiagnosticArray, self.check_status)
self.alert_sent = False
def check_status(self, msg):
for status in msg.status:
if status.level > 1: # WARNING or ERROR
if not self.alert_sent:
self.send_alert(status.name, status.message)
self.alert_sent = True
else:
self.alert_sent = False
def send_alert(self, name, message):
msg = MIMEText(f"Alert: {name} - {message}")
msg['Subject'] = 'ROS System Alert'
msg['From'] = 'robot@example.com'
msg['To'] = 'admin@example.com'
try:
server = smtplib.SMTP('smtp.example.com', 587)
server.send_message(msg)
server.quit()
except Exception as e:
rospy.logerr("Failed to send alert: %s", e)
if __name__ == '__main__':
rospy.init_node('alert_system')
system = AlertSystem()
rospy.spin()
94. 如何实现 ROS 系统的性能分析?
答案:
#!/usr/bin/env python
import rospy
import time
from collections import defaultdict
class PerformanceProfiler:
def __init__(self):
self.timings = defaultdict(list)
self.start_time = time.time()
def start_timer(self, name):
self.timings[name].append(('start', time.time()))
def end_timer(self, name):
self.timings[name].append(('end', time.time()))
def print_report(self):
print("\n=== Performance Report ===")
for name, events in self.timings.items():
total_time = 0
for i in range(0, len(events), 2):
if i+1 < len(events):
total_time += events[i+1][1] - events[i][1]
print(f"{name}: {total_time:.4f}s")
print("=========================\n")
# 使用示例
profiler = PerformanceProfiler()
def my_callback(msg):
profiler.start_timer("processing")
# 处理逻辑
time.sleep(0.01)
profiler.end_timer("processing")
profiler.start_timer("publishing")
# 发布逻辑
time.sleep(0.005)
profiler.end_timer("publishing")
# 定期打印报告
rospy.Timer(rospy.Duration(10), lambda e: profiler.print_report())
95. 如何实现 ROS 系统的故障恢复?
答案:
#!/usr/bin/env python
import rospy
import subprocess
import time
class RecoveryManager:
def __init__(self):
self.monitored_nodes = {
'/sensor_node': {'restart_count': 0, 'last_restart': 0},
'/control_node': {'restart_count': 0, 'last_restart': 0}
}
self.timer = rospy.Timer(rospy.Duration(5.0), self.check_nodes)
def check_nodes(self, event):
for node_name, info in self.monitored_nodes.items():
if not self.is_node_alive(node_name):
rospy.logerr(f"Node {node_name} is not alive!")
if time.time() - info['last_restart'] > 30: # 防止频繁重启
self.restart_node(node_name)
info['restart_count'] += 1
info['last_restart'] = time.time()
def is_node_alive(self, node_name):
try:
result = subprocess.run(['rosnode', 'ping', node_name, '-c', '1'],
capture_output=True, timeout=2)
return result.returncode == 0
except:
return False
def restart_node(self, node_name):
rospy.loginfo(f"Restarting {node_name}")
# 停止节点
subprocess.run(['rosnode', 'kill', node_name])
time.sleep(1)
# 重新启动(需要知道如何启动该节点)
# subprocess.Popen(['rosrun', 'my_package', 'my_node'])
if __name__ == '__main__':
rospy.init_node('recovery_manager')
manager = RecoveryManager()
rospy.spin()
96. 如何实现 ROS 系统的版本控制?
答案:
# 使用 git 管理 ROS 包
cd ~/catkin_ws/src
git init
git add .
git commit -m "Initial commit"
# 使用 rosinstall 管理多包
rosinstall src /path/to/rosinstall.rosinstall
# rosinstall 文件示例
- git:
local-name: my_package
uri: https://github.com/your/my_package.git
version: master
97. 如何实现 ROS 系统的文档生成?
答案:
# 使用 rosdoc_lite
sudo apt-get install ros-<distro>-rosdoc-lite
# 为包生成文档
cd ~/catkin_ws
rosdoc_lite src/my_package
# 查看生成的文档
firefox src/my_package/doc/html/index.html
98. 如何实现 ROS 系统的打包发布?
答案:
# 1. 准备包
cd ~/catkin_ws/src/my_package
# 2. 确保 package.xml 完整
cat package.xml
# 3. 创建 Debian 包
cd ~/catkin_ws
catkin_make --force-cmake -G"Eclipse CDT4 - Unix Makefiles" -DCMAKE_BUILD_TYPE=Release
sudo checkinstall
# 4. 或使用 bloom
sudo apt-get install python3-bloom
bloom-generate rosdebian --os-name ubuntu --os-version focal --distro noetic
dpkg-buildpackage -us -uc
99. 如何实现 ROS 系统的迁移(ROS1 到 ROS2)?
答案: 主要变化:
- 通信机制:话题/服务 → DDS
- 构建系统:catkin → ament
- Python 版本:Python 2 → Python 3
- 节点管理:Master → Discovery
迁移步骤:
- 更新代码到 Python 3
- 修改 CMakeLists.txt
- 更新消息/服务定义
- 修改节点启动方式
- 测试和验证
100. ROS 面试常见问题及答案总结
Q1: 什么是 ROS? A: ROS 是机器人操作系统,提供分布式通信、工具和库的机器人开发框架。
Q2: 话题和服务的区别? A: 话题异步、单向、多对多;服务同步、双向、一对一。
Q3: 如何启动多个节点?
A: 使用 launch 文件:<node pkg="..." type="..." name="..."/>
Q4: 如何查看节点通信?
A: 使用 rqt_graph 或 rosnode info。
Q5: 如何处理命名冲突? A: 使用命名空间和私有名称。
Q6: 什么是 TF? A: 管理坐标系变换的系统。
Q7: 如何调试 ROS 节点?
A: 使用 gdb、pdb、rqt_console、rostopic 等工具。
Q8: 如何实现多机通信?
A: 设置 ROS_MASTER_URI 和 ROS_IP 环境变量。
Q9: 什么是 Nodelet? A: 在同一进程中运行多个节点,减少通信开销。
Q10: 如何优化 ROS 系统性能? A: 使用 Nodelet、调整队列大小、优化消息类型、使用共享内存。
总结
这份 100 道 ROS 基础题目涵盖了从入门到进阶的全部核心知识点。掌握这些内容,你将能够:
- 理解 ROS 架构:节点、话题、服务、消息等核心概念
- 熟练使用命令:rosnode、rostopic、rosservice 等常用命令
- 开发 ROS 程序:编写节点、launch 文件、自定义消息
- 调试和优化:使用各种工具诊断和提升系统性能
- 应对面试:回答常见的 ROS 技术问题
学习建议:
- 动手实践每道题目
- 搭建实际的 ROS 系统
- 阅读优秀的开源 ROS 项目
- 参与 ROS 社区讨论
祝你在 ROS 的学习和面试中取得成功!
