实践入门ROS—机器人操作系统
ROS(Robot Operating System,机器人操作系统)是一个为机器人软件开发提供支持的灵活框架。它为机器人软件开发者提供了一套广泛的工具和库,使得他们能够更容易地构建和运行机器人应用。
1. 环境设置
- 安装ROS:
- 用docker快速安装ROS的基本版本。
- 设置环境变量。
2. 基本概念
- 节点 (Nodes):
- 创建一个简单的节点。
- 运行节点,并观察输出。
- 主题 (Topics):
- 创建一个简单的主题发布者和订阅者。
- 发布和订阅消息。
3. 实践项目
- 创建一个简单的机器人模拟:
- 选择一个简单的机器人模型,如一个两轮机器人。
- 在ROS中加载机器人模型。
- 使用Gazebo控制机器人移动并监控机器人的状态和位置。
这个计划需要你在一个实际的ROS环境中操作。如果你在任何步骤中遇到问题,ROS社区和在线论坛可以为你提供帮助。
环境设置
用docker快速安装ROS的基本版本
使用Docker可以简化ROS的安装过程,同时保持系统的整洁。以下是使用Docker快速安装ROS的基本版本的步骤:
1.安装Docker 首先,你需要在你的机器上安装Docker。如果你还没有安装Docker,你可以从Docker官网下载并安装。
2.创建ROS的Docker镜像
由于后面我们需要使用rviz,所以我们通过Dockerfile来创建镜像而不是简单的拉取ros镜像;
在合适的位置创建一个Dockerfile:
文件名:Dockerfile
# 使用官方的ROS镜像作为基础镜像
FROM ros:noetic-ros-base
# 更新apt包列表
RUN apt-get update
# 安装 RViz 和其他 ROS 工具
RUN apt-get install -y \
ros-noetic-rviz \
ros-noetic-xacro \
ros-noetic-joint-state-publisher-gui \
ros-noetic-robot-state-publisher
# 尝试单独安装 Gazebo 9
RUN apt-get install -y \
gazebo9 \
libgazebo9-dev
# 安装 Gazebo ROS 包
RUN apt-get install -y \
ros-noetic-gazebo-ros-pkgs \
ros-noetic-gazebo-ros-control
# 设置容器的主命令为bash终端
CMD ["/bin/bash"]
然后打开cmd进入相同的目录,然后执行:(需要设置代理)
docker build -t my-ros .
![](https://www.geediy.net/wp-content/uploads/2024/05/121.png)
3.Windows中安装和启动VcXsrv 首先需要下载并安装 VcXsrv。安装过程中可能需要选择完整安装,并指定目标文件夹1。
运行 XLaunch:通过运行 VcXsrv 中的 XLaunch 程序来启动 X 服务器。在显示设置中,选择“多窗口”选项,并点击下一步。在客户端启动中,检查“不启动客户端”选项,并点击下一步。在额外设置中,确保启用“禁用访问控制”,并点击下一步3,直到完成。后续启动rviz就会自动弹出窗口了。
![](https://www.geediy.net/wp-content/uploads/2024/05/23-2.png)
4.运行ROS容器 现在,你可以运行一个ROS容器。使用下面的命令来启动一个新的容器,并进入到bash shell:
docker run -it --name ros_container -v F:\DockerFile\ros:/data -e DISPLAY=host.docker.internal:0 my-ros-rviz:latest bash
以上命令会启动一个新的ROS容器,并为你提供一个bash shell,你可以在这个shell中运行ROS命令。-it
参数让Docker在交互模式下运行,-v F:\DockerFile\ros:/data
。由于不建议容器内安装编辑器,所以我们通过这个共享地址来修改和添加文件。-e DISPLAY=host.docker.internal:0
这个是前一步设置的显示窗口的地址。
如果关掉了容器想要重新连接可以执行 docker start <容器名>
和 docker attach <容器名>
5.创建ROS工作空间 在ROS容器中,你需要创建一个新的工作空间来存放你的ROS项目。首先,创建一个新的目录来作为你的工作空间:
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
然后,你可以构建你的工作空间:
cd ~/catkin_ws
catkin_make
以上步骤会创建一个名为 catkin_ws
的新工作空间,并初始化它。你现在可以开始在这个工作空间中创建和运行ROS项目了。
![](https://www.geediy.net/wp-content/uploads/2024/05/34.png)
文件名:ros_environment.sh
#!/bin/bash
# Source ROS setup script
source /opt/ros/noetic/setup.bash
# Source workspace setup script
source ~/catkin_ws/devel/setup.bash
# Set ROS Master URI
# Replace 'localhost' with your ROS Master's IP if it's on a different machine
export ROS_MASTER_URI=http://localhost:11311
# Set ROS Hostname
# Replace 'localhost' with this machine's IP if this machine is not the ROS Master
export ROS_HOSTNAME=localhost
# Set ROS IP
# Replace 'localhost' with this machine's IP
export ROS_IP=localhost
# Set Gazebo Plugin Path
export GAZEBO_PLUGIN_PATH=/opt/ros/noetic/lib:$GAZEBO_PLUGIN_PATH
使环境变量设置生效
- 保存上述脚本到
ros_environment.sh
文件。 - 通过以下命令使环境变量设置生效:
source ros_environment.sh
(需要在脚本所在目录,也就是/data
)
root@62d9227aa9ff:/data# ls
Dockerfile ros_environment.sh
root@62d9227aa9ff:/data# source ros_environment.sh
root@62d9227aa9ff:/data#
上述 ros_environment.sh
脚本执行了几个操作:
- 通过
source
命令,它加载了ROS和你的工作空间的设置脚本。 - 它设置了
ROS_MASTER_URI
,ROS_HOSTNAME
和ROS_IP
环境变量,以确保你的ROS节点可以正确地通信。
每次启动新的终端或ROS容器时,都应执行 source ros_environment.sh
命令以确保环境变量正确设置。为了简化这个过程,你可能想要将 source ros_environment.sh
命令添加到你的 .bashrc
文件中,这样它会在每次启动新终端时自动执行。
上面我们已经创建和构建好了工作空间。
基本概念
创建一个简单的节点
创建一个简单的ROS节点通常涉及创建一个新的ROS包和编写一些基本的ROS代码。以下是如何完成这些步骤的指南。
1.创建一个新的ROS包 首先,在你的工作空间的 src
目录中创建一个新的ROS包。我们将此包命名为 simple_node_package
,并依赖于 roscpp
和 std_msgs
:
cd ~/catkin_ws/src
catkin_create_pkg simple_node_package roscpp std_msgs
![](https://www.geediy.net/wp-content/uploads/2024/05/45-1.png)
2.编写简单的ROS节点 现在,在F:\DockerFile\ros
中创建一个名为 simple_node.cpp 的文件,并填写以下代码:
文件名:simple_node.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "simple_node");
// 创建节点句柄
ros::NodeHandle nh;
// 创建一个发布者对象
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>("chatter", 1000);
// 设置循环频率
ros::Rate loop_rate(10);
while (ros::ok())
{
// 创建一个消息对象
std_msgs::String msg;
msg.data = "Hello, ROS!";
// 发布消息
chatter_pub.publish(msg);
// 处理任何可用的回调函数
ros::spinOnce();
// 按照设定的频率休眠
loop_rate.sleep();
}
return 0;
}
然后将其复制到 simple_node_package 包中src目录中。
root@62d9227aa9ff:~/catkin_ws/src/simple_node_package# cp /data/simple_node.cpp ./src/
root@62d9227aa9ff:~/catkin_ws/src/simple_node_package# cd src
root@62d9227aa9ff:~/catkin_ws/src/simple_node_package/src# ls
simple_node.cpp
root@62d9227aa9ff:~/catkin_ws/src/simple_node_package/src#
3.修改CMakeLists.txt 为了编译你的节点,你需要修改 simple_node_package
包的 CMakeLists.txt
文件。把这两行代码加到文件的最后,
文件名:CMakeLists.txt
(原代码)
add_executable(simple_node src/simple_node.cpp)
target_link_libraries(simple_node ${catkin_LIBRARIES})
运行结果:
root@62d9227aa9ff:~/catkin_ws/src/simple_node_package# cp CMakeLists.txt /data
(Windows中修改代码)
root@62d9227aa9ff:~/catkin_ws/src/simple_node_package# cp /data/CMakeLists.txt .
root@62d9227aa9ff:~/catkin_ws/src/simple_node_package# cat CMakeLists.txt
...
## custom
add_executable(simple_node src/simple_node.cpp)
target_link_libraries(simple_node ${catkin_LIBRARIES})
4.构建你的工作空间 返回到你的工作空间的根目录,并运行 catkin_make
来构建你的工作空间和新节点:
cd ~/catkin_ws
catkin_make
![](https://www.geediy.net/wp-content/uploads/2024/05/67.png)
5.运行你的节点 首先,确保你的ROS核心正在运行:
![](https://www.geediy.net/wp-content/uploads/2024/05/78.png)
roscore
然后我们打开一个新的窗口运行这个命令打开一个新的终端:(可以在Docker Desktop中查看自己容器的名字)
docker exec -it <container_name> /bin/bash
在另一个终端中,运行你的新节点:
source /data/ros_environment.sh
rosrun simple_node_package simple_node
![](https://www.geediy.net/wp-content/uploads/2024/05/89.png)
运行节点,并观察输出
运行和观察ROS节点输出相对简单。在上一步中,你已经创建了一个名为 simple_node
的节点,它发布”Hello, ROS!”消息到 chatter
主题。现在我们将运行该节点并观察其输出。
1.观察输出 现在,我们和之前一样docker exec -it <container_name> /bin/bash
再打开第三个终端。
在第三个新的终端中,你可以使用 rostopic
命令来监听 chatter
主题并观察节点的输出:
source /data/ros_environment.sh
rostopic echo /chatter
你应该看到每秒10条”Hello, ROS!”消息,如下所示:
![](https://www.geediy.net/wp-content/uploads/2024/05/910.png)
2.(可选)查看节点和主题信息 你还可以使用 rosnode
和 rostopic
命令来获取有关运行中节点和主题的更多信息。
- 查看运行中的节点列表:
rosnode list
- 查看特定节点的信息(例如
simple_node
):rosnode info /simple_node
- 查看特定主题的信息(例如
chatter
):rostopic info /chatter
通过这些命令,你可以检查节点和主题的状态,确保它们按预期运行,并了解ROS系统的运行情况。
![](https://www.geediy.net/wp-content/uploads/2024/05/1011.png)
创建一个简单的主题发布者和订阅者
在ROS中,节点可以通过发布和订阅主题来交换消息。下面我们将创建一个简单的主题发布者和订阅者。我们将继续使用之前创建的 simple_node_package
包。
1.创建发布者 首先,我们创建一个名为 publisher_node.cpp
的文件来发布消息到一个名为 chatter
的主题。
文件名:publisher_node.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int argc, char **argv)
{
ros::init(argc, argv, "publisher_node");
ros::NodeHandle nh;
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
while (ros::ok())
{
std_msgs::String msg;
msg.data = "Hello, ROS!";
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
}
return 0;
}
2.创建订阅者 接下来,我们创建一个名为 subscriber_node.cpp
的文件来订阅 chatter
主题并打印接收到的消息。(文件的操作和前面的一样,不再赘述)
文件名:subscriber_node.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "subscriber_node");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe("chatter", 1000, chatterCallback);
ros::spin();
return 0;
}
3.更新CMakeLists.txt 现在,更新 simple_node_package
包的 CMakeLists.txt
文件以包含新的节点。
文件名:CMakeLists.txt
(原来的代码)
add_executable(publisher_node src/publisher_node.cpp)
target_link_libraries(publisher_node ${catkin_LIBRARIES})
add_executable(subscriber_node src/subscriber_node.cpp)
target_link_libraries(subscriber_node ${catkin_LIBRARIES})
4.构建工作空间 返回到你的工作空间的根目录,并运行 catkin_make
来构建你的工作空间和新节点:
cd ~/catkin_ws
catkin_make
![](https://www.geediy.net/wp-content/uploads/2024/05/1112.png)
5.运行发布者和订阅者节点 确保你的ROS核心正在运行(前面说过的roscore
),然后在两个不同的终端中分别运行发布者和订阅者节点:
source ~/catkin_ws/devel/setup.bash
rosrun simple_node_package publisher_node
第二个终端:
source ~/catkin_ws/devel/setup.bash
rosrun simple_node_package subscriber_node
现在,你应该能在订阅者节点的终端中看到每秒10条”Hello, ROS!”消息,表明发布者和订阅者节点正在正确地交换消息。
![](https://www.geediy.net/wp-content/uploads/2024/05/1213.png)
发布和订阅消息
在ROS中,发布和订阅消息是节点间通信的基础。我们已经在上一个步骤中创建了一个简单的发布者和订阅者节点。现在我们将详细解释如何发布和订阅消息。
发布消息 在 publisher_node.cpp
中,我们创建了一个发布者来发布 std_msgs/String
类型的消息到 chatter
主题。
初始化ROS和创建节点句柄
ros::init(argc, argv, "publisher_node");
ros::NodeHandle nh;
创建发布者对象
ros::Publisher chatter_pub = nh.advertise<std_msgs::String>("chatter", 1000);
nh.advertise
方法创建了一个发布者对象,该对象可以发布消息到名为 chatter
的主题。1000
是队列大小,它确定了在订阅者能够处理之前可以缓冲的消息数量。
创建并发布消息
std_msgs::String msg;
msg.data = "Hello, ROS!";
chatter_pub.publish(msg);
每次循环时,我们都会创建一个新的 std_msgs/String
消息,并使用 publish
方法将其发布到 chatter
主题。
订阅消息
在 subscriber_node.cpp
中,我们创建了一个订阅者来订阅 chatter
主题并处理接收到的消息。
创建订阅者对象
ros::Subscriber sub = nh.subscribe("chatter", 1000, chatterCallback);
nh.subscribe
方法创建了一个订阅者对象,该对象订阅了名为 chatter
的主题。1000
是队列大小,它确定了可以在处理之前缓冲的消息数量。chatterCallback
是当接收到新消息时调用的回调函数。
定义回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
每当接收到新的 chatter
消息时,都会调用 chatterCallback
函数。这个函数简单地打印了接收到的消息。
运行节点
在运行节点并观察它们交换消息时,我们已经在上一个步骤中详细说明了具体的步骤。
这样,你就可以理解ROS中如何发布和订阅消息,以及如何在节点之间交换数据。
实践项目
选择一个简单的机器人模型,如一个两轮机器人。
在ROS中,通常使用URDF(Unified Robot Description Format,统一机器人描述格式)或者xacro(XML Macros,XML宏)来描述机器人模型。在这一步中,我们将创建一个简单的两轮机器人模型。
1.创建一个新的ROS包:
cd ~/catkin_ws/src
catkin_create_pkg your_robot_package roscpp rospy std_msgs sensor_msgs gazebo_ros gazebo_plugins
2.创建URDF文件 接下来,在新创建的 your_robot_package
包中创建一个名为 two_wheel_robot.urdf
的文件来描述你的两轮机器人。your_robot_package
将这个文件放到~/catkin_ws/src/your_robot_package/urdf/two_wheel_robot.urdf
cd ~/catkin_ws/src/your_robot_package
mkdir urdf
mv /path/to/two_wheel_robot.urdf urdf/
文件名:two_wheel_robot.urdf
<?xml version="1.0"?>
<robot name="two_wheel_robot">
<!-- Base Link -->
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.1" radius="0.2"/>
</geometry>
</visual>
<collision>
<geometry>
<cylinder length="0.1" radius="0.2"/>
</geometry>
</collision>
<inertial>
<mass value="10"/>
<inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.1"/>
</inertial>
</link>
<!-- Left Wheel -->
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="0 0.15 0" rpy="1.57079632679 0 0"/> <!-- Rotate the wheel to be perpendicular to the ground -->
<axis xyz="0 0 1"/>
</joint>
<link name="left_wheel">
<visual>
<geometry>
<cylinder length="0.05" radius="0.1"/>
</geometry>
</visual>
<collision>
<geometry>
<cylinder length="0.05" radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="1"/>
<inertia ixx="0.025" ixy="0" ixz="0" iyy="0.025" iyz="0" izz="0.025"/>
</inertial>
</link>
<!-- Right Wheel -->
<joint name="right_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="right_wheel"/>
<origin xyz="0 -0.15 0" rpy="1.57079632679 0 0"/> <!-- Rotate the wheel to be perpendicular to the ground -->
<axis xyz="0 0 1"/>
</joint>
<link name="right_wheel">
<visual>
<geometry>
<cylinder length="0.05" radius="0.1"/>
</geometry>
</visual>
<collision>
<geometry>
<cylinder length="0.05" radius="0.1"/>
</geometry>
</collision>
<inertial>
<mass value="1"/>
<inertia ixx="0.025" ixy="0" ixz="0" iyy="0.025" iyz="0" izz="0.025"/>
</inertial>
</link>
<gazebo>
<plugin name="gazebo_ros_diff_drive" filename="libgazebo_ros_diff_drive.so">
<updateRate>100.0</updateRate>
<leftJoint>left_wheel_joint</leftJoint>
<rightJoint>right_wheel_joint</rightJoint>
<wheelSeparation>0.3</wheelSeparation> <!-- This should be the distance between your wheels -->
<wheelDiameter>0.2</wheelDiameter> <!-- This should be the diameter of your wheels -->
<torque>20</torque> <!-- You may need to adjust this value -->
<commandTopic>cmd_vel</commandTopic>
<odometryTopic>odom</odometryTopic>
<odometryFrame>odom</odometryFrame>
<robotBaseFrame>base_link</robotBaseFrame>
</plugin>
</gazebo>
</robot>
在这个URDF文件中,我们定义了一个简单的两轮机器人,它有一个基础链接(base_link)和两个连续关节,每个关节连接一个轮子。
3.查看机器人模型 为了查看你的机器人模型,你可以使用 urdf
包中的 urdf_viewer
工具。
首先需要下载 urdf_tutorial
ros包,先在Windows中将该包下载到前面的数据卷中 F:\DockerFile\ros
,在Windows中对应位置执行 git clone https://github.com/ros/urdf_tutorial.git
,然后在容器中执行 cp -r /data/urdf_tutorial ~/catkin_ws/src
之后再在 ~/catkin_ws
目录执行 catkin_make
![](https://www.geediy.net/wp-content/uploads/2024/05/1314.png)
现在,运行以下命令来查看你的机器人模型:
roslaunch urdf_tutorial display.launch model:='$(find your_robot_package)/urdf/two_wheel_robot.urdf'
这个命令会启动RViz,并加载你的两轮机器人模型。在RViz中,你应该能看到你的两轮机器人模型,并可以通过旋转和缩放来检查它的各个部分。
通过创建和查看URDF文件,你可以获得对ROS中机器人模型描述的基本理解,并可以开始为你的两轮机器人添加更多复杂的特性和组件。
![](https://www.geediy.net/wp-content/uploads/2024/05/1415.png)
![](https://www.geediy.net/wp-content/uploads/2024/05/1516.png)
控制机器人移动。
控制机器人移动通常涉及编写一个ROS节点,该节点发布到机器人的速度命令主题。对于简单的两轮机器人,通常会有一个名为 cmd_vel
的主题,它接收 geometry_msgs/Twist
类型的消息来控制机器人的线速度和角速度。
1.创建控制节点 创建一个新的文件名为 robot_controller.cpp
的文件,在 your_robot_package/src
目录中。
文件名: robot_controller.cpp
#include "ros/ros.h"
#include "geometry_msgs/Twist.h"
int main(int argc, char **argv)
{
ros::init(argc, argv, "robot_controller");
ros::NodeHandle nh;
ros::Publisher cmd_vel_pub = nh.advertise<geometry_msgs::Twist>("cmd_vel", 10);
ros::Rate loop_rate(10);
while (ros::ok())
{
geometry_msgs::Twist cmd_vel_msg;
cmd_vel_msg.linear.x = -0.5; // 0.5 m/s forward
cmd_vel_msg.angular.z = 0.1; // 0.1 rad/s rotation
cmd_vel_pub.publish(cmd_vel_msg);
ros::spinOnce();
loop_rate.sleep();
}
return 0;
}
在这段代码中,我们创建了一个名为 robot_controller
的ROS节点,它发布 geometry_msgs/Twist
消息到 cmd_vel
主题,以控制机器人的线速度和角速度。
2.更新CMakeLists.txt 修改 your_robot_package
包的 CMakeLists.txt
文件以包含新的控制节点。
文件名: CMakeLists.txt
(原代码)
add_executable(robot_controller src/robot_controller.cpp)
target_link_libraries(robot_controller ${catkin_LIBRARIES})
3.构建你的工作空间 返回到你的工作空间的根目录,并运行 catkin_make
来构建你的工作空间和新控制节点:
cd ~/catkin_ws
catkin_make
4.运行控制节点 在一个新的终端中,运行你的控制节点来发布速度命令:(需要提前运行roscore
)
source ~/data/ros_environment.sh
rosrun your_robot_package robot_controller
现在,你的控制节点应该在 cmd_vel
主题上发布速度命令。
然后和上面一样,我们打开一个新的终端,然后查看 cmd_vel
主题的消息:
source ~/data/ros_environment.sh
rostopic echo /cmd_vel
![](https://www.geediy.net/wp-content/uploads/2024/05/1617.png)
使用Gazebo监控机器人的状态和位置。
创建一个启动文件 (launch
文件) 来加载机器人模型和控制器。在该包中的launch
文件夹(需新建)中创建一个文件名为robot.launch
的文件,并将以下内容复制到该文件中:
<launch>
<!-- Load the URDF into the ROS Parameter Server -->
<param name="robot_description" textfile="$(find your_robot_package)/urdf/two_wheel_robot.urdf" />
<!-- Launch Gazebo with the robot model -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="world_name" value="worlds/empty.world"/>
<arg name="paused" value="false"/>
</include>
<!-- Spawn robot model -->
<node name="spawn_urdf" pkg="gazebo_ros" type="spawn_model" args="-param robot_description -urdf -model two_wheel_robot" />
<!-- Load the controller -->
<node name="robot_controller" pkg="your_robot_package" type="robot_controller" output="screen"/>
</launch>
编译你的ROS包:
cd ~/catkin_ws
catkin_make
启动Gazebo和机器人控制器:
roslaunch your_robot_package robot.launch
在执行上述步骤后,你应该能够在Gazebo中看到你的机器人模型,并能够通过ROS来控制它。在robot_controller.cpp
中,你可以使用ROS的消息和服务来读取传感器数据、发送控制命令和与机器人交互。同时,你可以看到机器人在Gazebo中的实时模拟表现。
这只是一个基本的设置,具体的实现可能需要根据你的机器人模型和控制器代码进行调整。例如,你可能需要配置机器人的传感器和控制器,或者修改Gazebo和ROS的启动文件以适应你的特定需求。
![](https://www.geediy.net/wp-content/uploads/2024/05/v2-4cc8554adacf965a8e120c9a040097d9_b-1024x521.gif)