ROS通信,控制Gazebo-11机器人

ROS(Robot Operating System,机器人操作系统)是一个为机器人软件开发提供支持的灵活框架。它为机器人软件开发者提供了一套广泛的工具和库,使得他们能够更容易地构建和运行机器人应用。

学习计划

学习ROS (Robot Operating System) 在一小时内是非常紧凑的,但你可以通过实践来尽快掌握基础。以下是一个基本的一小时学习计划,以项目实践为目标:

  1. 环境设置
  • 安装ROS

  • 用docker快速安装ROS的基本版本。

  • 设置环境变量。

  1. 基本概念
  • 节点 (Nodes)

  • 创建一个简单的节点。

  • 运行节点,并观察输出。

  • 主题 (Topics)

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

  • 发布和订阅消息。

  1. 实践项目
  • 创建一个简单的机器人模拟

  • 选择一个简单的机器人模型,如一个两轮机器人。

  • 在ROS中加载机器人模型。

  • 使用Gazebo控制机器人移动并监控机器人的状态和位置。

这个计划需要你在一个实际的ROS环境中操作。

环境配置

docker快速安装ROS Gazebo

  • DockerDesktop安装

  • Dockerfile编写(参照Gazebo 上手 wsl)编译成image

  • Windows中安装和启动VcXsrv

    • 启动xLaunch
  • 运行Docker容器

  • 创建项目工作空间

    • 1
      2
      3
      mkdir -p ~/catkin_ws/src
      cd ~/catkin_ws/src
      catkin_init_workspace
  • 1
    2
    cd ~/catkin_ws
    catkin_make

配置环境变量

ros_environment.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/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

运行生效环境配置

1
source ros_environment.sh
  • 通过 source命令,它加载了ROS和你的工作空间的设置脚本。
  • 它设置了 ROS_MASTER_URIROS_HOSTNAMEROS_IP环境变量,以确保你的ROS节点可以正确地通信。

每次启动新的终端或ROS容器时,都应执行 source ros_environment.sh命令以确保环境变量正确设置。为了简化这个过程,你可能想要将 source ros_environment.sh命令添加到你的 .bashrc文件中,这样它会在每次启动新终端时自动执行。

基本概念

创建一个简单节点

1.创建一个新的ROS包 首先,在你的工作空间的 src 目录中创建一个新的ROS包。我们将此包命名为 simple_node_package,并依赖于 roscppstd_msgs

1
2
cd ~/catkin_ws/src
catkin_create_pkg simple_node_package roscpp std_msgs

2.编写简单的ROS节点 现在,创建一个名为 simple_node.cpp 的文件,并填写以下代码:

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
#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目录中。

3.修改CMakeLists.txt 为了编译你的节点,你需要修改 simple_node_package 包的 CMakeLists.txt 文件。把这两行代码加到文件的最后,

文件名:CMakeLists.txt

1
2
3
4
(原代码)

add_executable(simple_node src/simple_node.cpp)
target_link_libraries(simple_node ${catkin_LIBRARIES})

4.构建编译:

1
2
cd ~/catkin_ws
catkin_make

5 RosCore运行

1
roscore

6 新节点运行(在新的终端中)

1
2
3
4
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主题并观察节点的输出:

1
2
source /data/ros_environment.sh
rostopic echo /chatter

你应该看到每秒10条”Hello, ROS!”消息,如下所示:

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

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

创建一个简单的主题发布者和接收者

在ROS中,节点可以通过发布和订阅主题来交换消息。下面我们将创建一个简单的主题发布者和订阅者。我们将继续使用之前创建的 simple_node_package包。

1.创建发布者 首先,我们创建一个名为 publisher_node.cpp的文件来发布消息到一个名为 chatter的主题。

文件名:publisher_node.cpp

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 "std_msgs/String.h"

int main(int argc, char **argv)
{
ros::init(argc, argv, "publisher_node");
ros::NodeHandle nh;

// 创建了一个发布者对象,该对象可以发布消息到名为 chatter的主题。1000是队列大小,它确定了在订阅者能够处理之前可以缓冲的消息数量。
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); / /发布到 chatter主题

ros::spinOnce();
loop_rate.sleep();
}

return 0;
}

2.创建订阅者 接下来,我们创建一个名为 subscriber_node.cpp的文件来订阅 chatter主题并打印接收到的消息。(文件的操作和前面的一样,不再赘述)

文件名:subscriber_node.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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;
//订阅了名为 chatter的主题。1000是队列大小,它确定了可以在处理之前缓冲的消息数量。
ros::Subscriber sub = nh.subscribe("chatter", 1000, chatterCallback);

ros::spin();

return 0;
}

3.更新CMakeLists.txt 现在,更新 simple_node_package包的 CMakeLists.txt文件以包含新的节点。

文件名:CMakeLists.txt

1
2
3
4
5
6
7
(原来的代码)

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来构建你的工作空间和新节点:

1
2
cd ~/catkin_ws
catkin_make

5.运行发布者和订阅者节点 确保你的ROS核心正在运行(前面说过的roscore),然后在两个不同的终端中分别运行发布者和订阅者节点:

终端一

1
2
source ~/catkin_ws/devel/setup.bash
rosrun simple_node_package publisher_node

终端二

1
2
source ~/catkin_ws/devel/setup.bash
rosrun simple_node_package subscriber_node

现在,你应该能在订阅者节点的终端中看到每秒10条”Hello, ROS!”消息,表明发布者和订阅者节点正在正确地交换消息。

实践项目Robot controller

在ROS中,通常使用URDF(Unified Robot Description Format,统一机器人描述格式)或者xacro(XML Macros,XML宏)来描述机器人模型。在这一步中,我们将创建一个简单的两轮机器人模型。

两轮ROBOT模型

1 创建ROS pkg

1
2
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的文件来描述你的两轮机器人。

将这个文件放到~/catkin_ws/src/your_robot_package/urdf/two_wheel_robot.urdf

two_wheel_robot.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<?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包,解压放到~/catkin_ws/src/urdf_tutorial目录中。

然后执行

1
catkin_make

查看模型

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

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

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

控制ROBOT移动

控制机器人移动通常涉及编写一个ROS节点,该节点发布到机器人的速度命令主题。对于简单的两轮机器人,通常会有一个名为 cmd_vel的主题,它接收 geometry_msgs/Twist类型的消息来控制机器人的线速度和角速度。

1.创建控制节点 创建一个新的文件名为 robot_controller.cpp的文件,在 your_robot_package/src 目录中。

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
#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

1
2
3
4
(原代码)

add_executable(robot_controller src/robot_controller.cpp)
target_link_libraries(robot_controller ${catkin_LIBRARIES})

3.构建你的工作空间 返回到你的工作空间的根目录,并运行 catkin_make来构建你的工作空间和新控制节点:

1
2
cd ~/catkin_ws
catkin_make

4.运行控制节点 在一个新的终端中,运行你的控制节点来发布速度命令:(需要提前运行roscore

1
2
source ~/data/ros_environment.sh
rosrun your_robot_package robot_controller

现在,你的控制节点应该在 cmd_vel主题上发布速度命令。

然后和上面一样,我们打开一个新的终端,然后查看 cmd_vel主题的消息:

新终端

1
2
source ~/data/ros_environment.sh
rostopic echo /cmd_vel

Gazebo监控机器人的状况

创建一个启动文件 (launch文件) 来加载机器人模型和控制器。在该包中的launch文件夹(需新建)中创建一个文件名为robot.launch的文件,并将以下内容复制到该文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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包:

1
2
cd ~/catkin_ws 
catkin_make

启动Gazebo和机器人控制器:

1
roslaunch your_robot_package robot.launch

在执行上述步骤后,你应该能够在Gazebo中看到你的机器人模型,并能够通过ROS来控制它。在robot_controller.cpp中,你可以使用ROS的消息和服务来读取传感器数据、发送控制命令和与机器人交互。同时,你可以看到机器人在Gazebo中的实时模拟表现。

这只是一个基本的设置,具体的实现可能需要根据你的机器人模型和控制器代码进行调整。例如,你可能需要配置机器人的传感器和控制器,或者修改Gazebo和ROS的启动文件以适应你的特定需求。