学习内容:ROS的第一部分学习

  1. Node节点和Package包
  2. Topic话题与Message消息
  3. 发布订阅节点
  4. launch启动多个节点
  5. 机器人运动控制
  6. 激光雷达

学习时间:

  • 2026.3.15

学习产出:

一、Node节点和Package包
1.1节点与包
node是ROS最基本的程序单元,节点组织形式是package。包为node的载体,包里面有多个节点。ROS使用CMake和catkin作为编译工具。包是节点的容器。
1.2第一个Node节点
创建的节点需要放在包中。
1.2.1创建package包(流程)

工作空间建立

第一步

每一部分代表内容

依赖项就是容易出现在多个包内的节点的功能包,比如rospy,roscpp,std_msgs(标准消息包)。package.xml存在极有可能是package包。
回访依赖项。roscd可以跳转到功能包的地址,例如roscd roscpp。
1.2.2创建节点(src和scripts下的内容就是节点)
src目录下创建文件.cpp。在CMake.list中加入编译规则,如下

在这里插入图片描述
启动ros的核心:roscore(工作空间的环境参数一定要加入到终端程序里),其实ros里的节点就是一个应用程序。
要想使用ros::init的话需要在CMake.list中加入如下内容

在这里插入图片描述

ctrl+shift+w强制关闭终端。::当成_。响应外部信号需要写成ros::ok(),不能使用true。
示例代码:
#include <ros/ros.h>
int main(int argc, char *argv[])
{
ros::init(argc, argv, “chao_node”);
printf(“我的枪去而复返,你的生命有去无回!\n”);
while (ros::ok())
{
printf(“别投,我还能秀!\n”);
}
return 0;
}
2、Topic话题与Message消息(节点通讯方式)

在这里插入图片描述

小结
话题Topic是节点间进行持续通讯的一种形式。
话题通讯的两个节点通过话题的名称建立起话题通讯连接。
话题中通讯的数据,叫做消息Message。
消息Message通常会按照一定的频率持续不断的发送,以 ensure 消息数据的 real time性。
消息的发送方叫做话题的发布者Publisher。
消息的接收方叫做话题的订阅者Subscriber。

外延补充
一个ROS节点网络中,可以同时存在多个话题。
一个话题可以有多发布者,也可以有多个订阅者。
一个节点可以对多个话题进行订阅,也可以发布多个 topics。
不不同的传感器消息通常会拥有各自独立话题名称,每个话题只有一个发布者。
机器人的速度指令通常会有多个发布者,但是同一时间只能有一个发言人。

index.ros.org这个是学习ros的网站

2.2.1发布者C++实现

// 头文件
#include <ros/ros.h>
// 包含std_msgs/ String类型
#include <std_msgs/String.h>

int main(int argc, char *argv[])
{
    // 初始化ROS节点
    ros::init(argc,160
    // 初始化ROS节点
    ros::init(argc, argv, "chao_node");
    // 打印启动提示
    printf("我的枪去而复返,你的生命有去无回!\n");

    // 创建节点句柄
    ros::NodeHandle nh;
    // 创建发布者,发布类型为 std_msgs:: String, 话题名为“kuaishangchekaiheiqun”,队列长度为10
    ros::Publisher pub = nh.advertise<std_msgs:: String>("kuaishangchekaiheiqun", 10);
	ros::Rate loop_rate(10);
    while (ros::ok())
    {
        // 打印循环提示
        printf("我要开始刷屏了!\n");
        // 创建消息对象
        std_msgs:: String msg;
        // 赋值消息内容
        msg.data = "国服马超,带飞";
        // 发布消息
        pub.publish(msg);
        loop_rate.sleep();
    }

    return 0;
}

rostopic list用于查看活跃话题名称,rostopic echo /话题名称,可以查看话题内容rostopic hz /话题名称可以查看话题发送频率

小结
确定话题名称和消息类型。
在代码文件中include消息类型对应的头文件。
在main函数中通过NodeHandle大管家发布一个话题并得到消息发送对象。
生成要发送的消息包并进行发送数据的赋值。
调用消息发送对象的publish()函数将消息包发送到话题当中。

常用工具
rostopic list
列出当前系统中所有活跃着的话题。
rostopic echo 主题名称
显示指定话题中发送的消息包内容。
rostopic hz 主题名称
统计指定话题中消息包发送频率。

2.2.2订阅者C++实现

#include <ros/ros.h>
#include <std_msgs/String.h>

void chao_callback(std_msgs::String msg)
{
    printf(msg.data.c_str());
    printf("\n");
}

int main(int argc, char *argv[])
{
    ros::init(argc, argv, "ma_node");

    ros::NodeHandle nh;
    ros::Subscriber sub = nh.subscribe("kuai_shang_che_kai_hei_qun", 10, chao_callback);

    while (ros::ok())
    {
        ros::spinOnce();
    }

    return 0;
}

rqt_graph用于查看节点及话题通讯关系

话题的订阅
确定话题名称和消息类型。
在代码文件中include <ros.h>和消息类型对应的头文件。
在main函数中通过NodeHandler大管家订阅一个话题并设置消息接收回调函数。
定义一个回调函数,对接收到的消息包进行处理。
main函数中需要执行ros::spinOnce(),让回调函数能够响应接收到的消息包。
2.2.3发布者Python实现
新建scripts文件夹

#!/usr/bin/env python3
#coding=utf-8

import rospy
from std_msgs.msg import String

if __name__ == "__main__":
    rospy.init_node("chao_node")
    rospy.logwarn("我的枪去而复返,你的生命有去无回!")

    pub = rospy.Publisher("kuai_shang_che_kai_hei_qun", String, queue_size=10)

    rate = rospy.Rate(10)

    while not rospy.is_shutdown():
        rospy.loginfo("我要开始刷屏了")
        msg = String()
        msg.data = "国服马超,带飞"
        pub.publish(msg)
        rate.sleep()

注意:要添加可执行权限
2.2.4订阅者Python实现

#!/usr/bin/env python3
#coding=utf-8

import rospy
from std_msgs.msg import String

def chao_callback(msg):
    rospy.loginfo(msg.data)

def yao_callback(msg):
    rospy.logwarn(msg.data)

if __name__ == "__main__":
    rospy.init_node("ma_node")

    sub = rospy.Subscriber("kuai_shang_che_kai_hei_qun", String, chao_callback, queue_size=10)
    sub_2 = rospy.Subscriber("gie_gie_dai_wo", String, yao_callback, queue_size=10)

    rospy.spin()


3.launch启动多个节点
大纸盒套小纸盒
在这里插入图片描述

  1. 使用launch文件,可以通过roslaunch指令一次启动多个节点。
  2. 在launch文件中,为节点添加 output=“screen” 属性,可以让节点信息输出在终端中。(ROS_WARN不受该属性控制)
  3. 在launch文件中,为节点添加 launch-prefix=“gnome-terminal -e” 属性,可以让节点单独运行在一个独立终端中。
  4. 机器人运动控制

在这里插入图片描述

4.1.1C++实现机器人运动控制

#include <ros/ros.h>
#include <geometry_msgs/Twist.h>

int main(int argc, char *argv[])
{
    ros::init(argc, argv, "vel_node");

    ros::NodeHandle n;
    ros::Publisher vel_pub = n.advertise<geometry_msgs::Twist>("/cmd_vel", 10);

    geometry_msgs::Twist vel_msg;
    vel_msg.linear.x = 0.1;
    vel_msg.linear.y = 0;
    vel_msg.linear.z = 0;
    vel_msg.angular.x = 0;
    vel_msg.angular.y = 0;
    vel_msg.angular.z = 0;

    ros::Rate r(30);
    while (ros::ok())
    {
        vel_pub.publish(vel_msg);
        r.sleep();
    }

    return 0;
}

在这里插入图片描述

4.1.2Python实现机器人运动控制

#!/usr/bin/env python3
#coding=utf-8

import rospy
from geometry_msgs.msg import Twist

if __name__ == "__main__":
    rospy.init_node("vel_node")

    vel_pub = rospy.Publisher("cmd_vel", Twist, queue_size=10)

    vel_msg = Twist()
    vel_msg.linear.x = 0.1

    rate = rospy.Rate(30)
    while not rospy.is_shutdown():
        vel_pub.publish(vel_msg)
        rate.sleep()

5.雷达(话题是/scan)
5.1.1获取激光雷达数据C++节点
读取正前方距离值

#include <ros/ros.h>
#include <sensor_msgs/LaserScan.h>

void LidarCallback(const sensor_msgs::LaserScan msg)
{
    float fMidDist = msg.ranges[180];
    ROS_INFO("前方测距 ranges[180] = %f 米", fMidDist);
}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc, argv, "lidar_node");

    ros::NodeHandle n;
    ros::Subscriber lidar_sub = n.subscribe("/scan", 10, &LidarCallback);

    ros::spin();
    
    return 0;
}

5.1.2获取激光雷达数据Python节点

#!/usr/bin/env python3
#coding=utf-8

import rospy
from sensor_msgs.msg import LaserScan

def LidarCallback(msg):
    dist = msg.ranges[180]
    rospy.loginfo("前方测距 ranges[180] = %f 米", dist)

if __name__ == "__main__":
    rospy.init_node("lidar_node")
    lidar_sub = rospy.Subscriber("/scan", LaserScan, LidarCallback, queue_size=10)
    rospy.spin()

5.2.1激光雷达避障C++实现

#include <ros/ros.h>
#include <sensor_msgs/LaserScan.h>
#include <geometry_msgs/Twist.h>

ros::Publisher vel_pub;

void LidarCallback(const sensor_msgs::LaserScan &msg)
{
    float fMidDist = msg.ranges[180];
    ROS_INFO("前方测距 ranges[180] = %f 米", fMidDist);

    geometry_msgs::Twist vel_cmd;
    if (fMidDist < 1.5)
    {
        vel_cmd.angular.z = 0.3;
    }
    else
    {
        vel_cmd.linear.x = 0.05;
    }
    vel_pub.publish(vel_cmd);
}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL, "");
    ros::init(argc, argv, "lidar_node");

    ros::NodeHandle n;
    ros::Subscriber lidar_sub = n.subscribe("/scan", 10, &LidarCallback);
    vel_pub = n.advertise<geometry_msgs::Twist>("/cmd_vel", 10);

    ros::spin();

    return 0;
}

5.2.2激光雷达避障Python实现


```cpp

```python
#!/usr/bin/env python3
#coding=utf-8

import rospy
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist

def LidarCallback(msg):
    global vel_pub
    dist = msg.ranges[180]
    rospy.loginfo("前方测距 ranges[180] = %f 米", dist)

    vel_cmd = Twist()
    if dist < 1.5:
        vel_cmd.angular.z = 0.3
    else:
        vel_cmd.linear.x = 0.05
    vel_pub.publish(vel_cmd)

if __name__ == "__main__":
    rospy.init_node("lidar_node")
    lidar_sub = rospy.Subscriber("/scan", LaserScan, LidarCallback, queue_size=10)
    vel_pub = rospy.Publisher("/cmd_vel", Twist, queue_size=10)
    rospy.spin()

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐