实践入门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 .

3.Windows中安装和启动VcXsrv 首先需要下载并安装 VcXsrv。安装过程中可能需要选择完整安装,并指定目标文件夹​1​。

运行 XLaunch:通过运行 VcXsrv 中的 XLaunch 程序来启动 X 服务器。在显示设置中,选择“多窗口”选项,并点击下一步。在客户端启动中,检查“不启动客户端”选项,并点击下一步。在额外设置中,确保启用“禁用访问控制”,并点击下一步​3​,直到完成。后续启动rviz就会自动弹出窗口了。

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项目了。

文件名: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_URIROS_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

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

5.运行你的节点 首先,确保你的ROS核心正在运行:

roscore
然后我们打开一个新的窗口运行这个命令打开一个新的终端:(可以在Docker Desktop中查看自己容器的名字)

docker exec -it <container_name> /bin/bash
在另一个终端中,运行你的新节点:

source /data/ros_environment.sh
rosrun simple_node_package simple_node

运行节点,并观察输出

运行和观察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!”消息,如下所示:

2.(可选)查看节点和主题信息 你还可以使用 rosnode和 rostopic命令来获取有关运行中节点和主题的更多信息。

  • 查看运行中的节点列表:rosnode list
  • 查看特定节点的信息(例如 simple_node): rosnode info /simple_node
  • 查看特定主题的信息(例如 chatter):rostopic info /chatter

通过这些命令,你可以检查节点和主题的状态,确保它们按预期运行,并了解ROS系统的运行情况。

创建一个简单的主题发布者和订阅者

在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

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!”消息,表明发布者和订阅者节点正在正确地交换消息。

发布和订阅消息

在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

现在,运行以下命令来查看你的机器人模型:

roslaunch urdf_tutorial display.launch model:='$(find your_robot_package)/urdf/two_wheel_robot.urdf'

这个命令会启动RViz,并加载你的两轮机器人模型。在RViz中,你应该能看到你的两轮机器人模型,并可以通过旋转和缩放来检查它的各个部分。

通过创建和查看URDF文件,你可以获得对ROS中机器人模型描述的基本理解,并可以开始为你的两轮机器人添加更多复杂的特性和组件。

控制机器人移动。

控制机器人移动通常涉及编写一个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

使用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的启动文件以适应你的特定需求。

Similar Posts

Leave a Reply