ROS2学习

news/2025/2/24 2:02:24

前言

本篇文章属于ROS2humble的学习笔记,来源于B站鱼香ROSup主。下面是这位up主的视频链接。本文为个人学习笔记,只能做参考,细节方面建议观看视频,肯定受益匪浅。

《ROS 2机器人开发从入门到实践》课程介绍_哔哩哔哩_bilibili

 一、Python节点的订阅和发布

1.下载小说,并通过话题间隔5s发送一行

(1)创建工作空间

在终端中输入:

mkdir -p topic_ws/src

(2)在src目录下创建软件包

处于src目录下,在终端中输入:

ros2 pkg create demo_python_topic --build-type ament_python --dependencies rclpy example_interfaces --license Apache-2.0

回到topic_ws目录下,再输入:

colcon build                 构建文件

 (3)在有__init__.py文件的目录下创建节点文件novel_pub_node.py,节点代码部分如下

import rclpy
from rclpy.node import Node
import requests
from example_interfaces.msg import String
from queue import Queue

class NovelNode(Node):
    def __init__(self, node_name):
        super().__init__(node_name)
        self.get_logger().info(f'{node_name},启动!')
        self.novel_queue = Queue()  #创建队列
        self.novel_publisher = self.create_publisher(String, 'novel', 100)
        self.create_timer(5, self.timer_callback)

    def timer_callback(self):
        if self.novel_queue.qsize()>0:
            line = self.novel_queue.get()
            msg = String()
            msg.data = line
            self.novel_publisher.publish(msg)
            self.get_logger().info(f'发布了:{msg}')

    def download(self, url):
        responese = requests.get(url)
        responese.encoding = 'utf-8'
        text = responese.text
        self.get_logger().info(f'下载{url},{len(text)}')
        for line in text.splitlines():
            self.novel_queue.put(line)


def main():
    rclpy.init()
    node = NovelNode('novel_pub')
    node.download('https://fanqienovel.com/reader/7173216089122439711?enter_from=page')
    rclpy.spin(node)
    rclpy.shutdown()

(4)执行代码

先找到

entry_points={

'console_scripts': [

],

},

改为

entry_points={

        'console_scripts': [

                'novel_pub_node=demo_python_topic.novel_pub_node:main'

],

},

等号左边是可执行文件的名字,等号右边是软件包名和节点名

然后回到 topic_ws目录下,再输入:

colcon build                 构建文件

再在终端中输入source install/setup.bash

修改一下环境变量

再运行即可:ros2 run demo_python_topic novel_pub_node

2.订阅小说并合成语音

import espeakng
import rclpy
from rclpy.node import Node
from example_interfaces.msg import String
from queue import Queue
import threading
import time

class NovelSubNode(Node):
    def __init__(self, node_name):
        super().__init__(node_name)
        self.get_logger().info(f'{node_name},启动!')
        self.novel_queue = Queue()
        self.create_subscription(String, 'novel', self.novel_callback, 10)
        self.speech_thread_ = threading.Thread(target=self.speaker_thread)
        self.speech_thread_.start()

    def novel_callback(self, msg):
        self.novel_queue.put(msg.data)
        pass

    def speaker_thread(self):
        speaker = espeakng.Speaker()
        speaker.voice = 'zh'

        while rclpy.ok():   #检测ROS当前上下文是否ok
            if self.novel_queue.qsize()>0:
                text = self.novel_queue.get()
                self.get_logger().info(f'朗读:{text}')
                speaker.say(text)       #说
                speaker.wait()        #等说完
            else:
                #让当前线程休眠1s
                time.sleep(1)

def main():
    rclpy.init()
    node = NovelSubNode('novel_sub')
    rclpy.spin(node)
    rclpy.shutdown()

espeakng为朗读引入的库

注意当前线程休眠1s的操作很关键,这能降低CPU功耗

 二、C++节点的订阅和发布

1.发布速度控制海龟画圆

(1)在工作空间的src目录下创建软件包

处于src目录下,在终端中输入:

ros2 pkg create demo_cpp_topic --build-type ament_cmake --dependencies rclcpp geometry_msgs turtlesim  --license Apache-2.0

(2)在软件包下的src目录下创建节点文件turtle_circle.cpp

出现该报错要配置includePath

配置成这样即可

节点代码如下:

#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include <chrono>

using namespace std::chrono_literals;

class TurtleCircleNode: public rclcpp::Node
{
private:
    rclcpp::TimerBase::SharedPtr timer_;
    rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_;//发布者的共享指针


public:
    explicit TurtleCircleNode(const std::string& node_name):Node(node_name)
    {
        publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel",10);
        timer_ = this->create_wall_timer(1000ms,std::bind(&TurtleCircleNode::timer_callback,this));
    }

    void timer_callback()
    {
        auto msg = geometry_msgs::msg::Twist();
        msg.linear.x = 1.0;
        msg.angular.z = 0.5;
        publisher_->publish(msg);
    }
}; 

int main(int argc,char *argv[])
{
    rclcpp::init(argc,argv);
    auto node = std::make_shared<TurtleCircleNode>("turtle_circle");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

(4)执行代码

首先去CMakeLists文件

add_executable(turtle_circle src/turtle_circle.cpp) #添加可执行文件

ament_target_dependencies(turtle_circle rclcpp geometry_msgs)        #添加依赖

install(TARGETS turtle_circle

DESTINATION lib/${PROJECT_NAME}

) #拷贝文件到人ros2 run的文件目录上,便于ros2 run启动

 然后回到topic_ws工作空间下colcon build 编译文件

生成完后输入source install/setup.bash配置环境变量

ros2 run demo_cpp_topic turtle_circle 即可运行

2.订阅pose实现闭环控制

#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include <chrono>
#include "turtlesim/msg/pose.hpp"

using namespace std::chrono_literals;

class TurtleControlNode: public rclcpp::Node
{
private:
    rclcpp::TimerBase::SharedPtr timer_;
    rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_;//发布者的共享指针
    rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr subscriber_;//订阅者的共享指针
    double target_x_{1.0};
    double target_y_{1.0};
    double k{0.5};
    double max_speed_{3.0};

public:
    explicit TurtleControlNode(const std::string& node_name):Node(node_name)
    {
        publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel",10);
        subscriber_ = this->create_subscription<turtlesim::msg::Pose>("/turtle1/pose",10,std::bind(&TurtleControlNode::sub_callback,this,std::placeholders::_1));
        // timer_ = this->create_wall_timer(1500ms,std::bind(&TurtleControlNode::timer_callback,this));
    }

    void sub_callback(const turtlesim::msg::Pose::SharedPtr pose)
    {
        //1. 获取当前位置
        auto current_x = pose->x;
        auto current_y = pose->y;
        RCLCPP_INFO(get_logger(),"当前:x=%f,y=%f",current_x,current_y);
        
        //2. 计算当前海龟位置跟目标位置之间的距离差和角度差
        auto distance = std::sqrt(
            (target_x_-current_x)*(target_x_-current_x)+
            (target_y_-current_y)*(target_y_-current_y)
        );
        
        auto angle = std::atan2((target_y_-current_y),(target_x_-current_x))-pose->theta;
        
        //3.控制策略
        auto msg = geometry_msgs::msg::Twist();
        if (distance>0.1)
        {
            if (fabs(angle)>0.2)
            {
                msg.angular.z = k*angle;
            }
            else
            {
                msg.linear.x = k*distance;
            }
        }


        //4.限制速度
        if (msg.linear.x > max_speed_)
        {
            msg.linear.x = max_speed_;
        }
        publisher_->publish(msg);
    }
}; 

int main(int argc,char *argv[])
{
    rclcpp::init(argc,argv);
    auto node = std::make_shared<TurtleControlNode>("turtle_control");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

三、话题通信最佳实践(获取系统信息并显示)

1.自定义消息类型

在src目录下创建status_interfaces功能包,在功能包目录下,创建msg文件夹,在msg文件夹下创建SystemStatus.msg文件

builtin_interfaces/Time stamp # 记录时间戳
string host_name #主机名字
float32 cpu_percent #CPU使用率
float32 memory_percent #内存使用率
float32 memory_total #内存总大小
float32 memory_available #内存剩余量
float64 net_sent #网络数据发送总量
float64 net_rece #网络数据接受总量

 在SystemStatus.msg文件中编写如上代码

在CMakeLists文件中添加cmake函数

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(builtin_interfaces REQUIRED)
find_package(rosidl_default_generators REQUIRED)
#cmake函数,来自依赖rosidl_default_generators 用于将msg消息接口定义文件转换成库或者头文件夹
rosidl_generate_interfaces(${PROJECT_NAME}
    "msg/SystemStatus.msg"
    DEPENDENCIES builtin_interfaces
)

再在package.xml文件中添加声明,声明该功能包是包含消息接口的功能包

<member_of_group>rosidl_interface_packages</member_of_group>

2.发布者节点代码

import rclpy
from status_interfaces.msg import SystemStatus
from rclpy.node import Node 
import psutil
import platform

class SysStatusPub(Node):
    def __init__(self,node_name):
        super().__init__(node_name)
        self.status_publisher_ = self.create_publisher(
            SystemStatus,'sys_status',10
        )
        self.timer_ = self.create_timer(1.0,self.timer_callback)
        
    def timer_callback(self):
        cpu_percent =  psutil.cpu_percent()
        memory_info = psutil.virtual_memory()
        net_io_counters = psutil.net_io_counters()
        
        msg = SystemStatus()
        msg.stamp = self.get_clock().now().to_msg()
        msg.host_name = platform.node()
        msg.cpu_percent = cpu_percent
        msg.memory_percent = memory_info.percent
        msg.memory_total = memory_info.total / 1024 / 1024
        msg.memory_available = memory_info.available / 1024 / 1024
        msg.net_sent = net_io_counters.bytes_sent / 1024 / 1024
        msg.net_recv = net_io_counters.bytes_recv / 1024 / 1024

        self.get_logger().info(f'发布:{str(msg)}')
        self.status_publisher_.publish(msg)

def main():
    rclpy.init()
    node = SysStatusPub('sys_status_pub')
    rclpy.spin(node)
    rclpy.shutdown()

3.订阅者节点代码

#include <QApplication>
#include <QLabel>
#include <QString>
#include <rclcpp/rclcpp.hpp>
#include "status_interfaces/msg/system_status.hpp"

using SystemStatus = status_interfaces::msg::SystemStatus;

class SysStatusDisplay : public rclcpp::Node
{
private:
    rclcpp::Subscription<SystemStatus>::SharedPtr subscriber_;
    QLabel *label_;

public:
    SysStatusDisplay() : Node("sys_status_display")
    {
        label_ = new QLabel();
        subscriber_ = this->create_subscription<SystemStatus>("sys_status", 10, [&]
            (const SystemStatus::SharedPtr msg) -> void
            { 
                label_->setText(get_qstr_from_msg(msg));

            });
        label_->setText(get_qstr_from_msg(std::make_shared<SystemStatus>()));
        label_->show();
    }

    QString get_qstr_from_msg(const SystemStatus::SharedPtr msg)
    {
        std::stringstream show_str;
        show_str << "=============状态可视化显示工具=============\n"
                 << "数 据 时 间:\t" << msg->stamp.sec << "\ts\n"
                 << "主 机 名 字\t" << msg->host_name << "\t\n"
                 << "CPU 使用率:\t" << msg->cpu_percent << "\t%\n"
                 << "内存使用率:\t" << msg->memory_percent << "\t%\n"
                 << "内存总大小:\t" << msg->memory_total << "\tMB\n"
                 << "剩余有效内存:\t" << msg->memory_available << "\tMB\n"
                 << "网络发送量:\t" << msg->net_sent << "\tMB\n"
                 << "网络接受量:\t" << msg->net_recv << "\tMB\n"
                 << "=========================================";
        return QString::fromStdString(show_str.str());                
    }
};

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    QApplication app(argc, argv);
    auto node = std::make_shared<SysStatusDisplay>();
    std::thread spin_thread([&]() -> void
                                {
                                    rclcpp::spin(node);
                                });
    spin_thread.detach();
    app.exec();
    return 0;
}

这里由于rclcpp::spin()和app.exec()都会阻塞代码进程,所以要多开一个线程

四、Git入门

 


http://www.niftyadmin.cn/n/5863924.html

相关文章

云手机如何进行经纬度修改

云手机如何进行经纬度修改 云手机修改经纬度的方法因不同服务商和操作方式有所差异&#xff0c;以下是综合多个来源的常用方法及注意事项&#xff1a; 通过ADB命令注入GPS数据&#xff08;适用于技术用户&#xff09; 1.连接云手机 使用ADB工具连接云手机服务器&#xff0c;…

Android SoundTrigger架构学习

本文将个人针对Android SoundTrigger的学习做了简单总结&#xff0c;以方便后续查询和学习。 一、架构解析 1、Android SoundTrigger架构如下&#xff08;简图&#xff09; 1.1、简单介绍 1&#xff09;SoundTriggerHelper SoundTriggerHelper是一个应用辅助工具类&#xf…

MySQL后端返回给前端的时间变了(时区问题)

问题&#xff1a;MySQL里的时间例如为2025-01-10 21:19:30&#xff0c;但是返回到前端就变成了2025-01-10 13:19:30&#xff0c;会出现小时不一样或日期变成隔日的问题 一般来说设计字段时会使用datetime字段类型&#xff0c;这是一种用于时间的字段类型&#xff0c;而这个类型…

安卓系统远程控制电脑方法,手机远控教程,ToDesk工具

不知道大家有没有觉得手机、平板虽然很好用&#xff0c;却也仍存在有很多替代不了电脑的地方。就比如说撰写文档、做数据报表啥的就不如PC端操作般方便&#xff0c;就跟别说PS修图、AE视频剪辑等需高性能设备来带动才易用的了。 好在也是有对策可解决&#xff0c;装个ToDesk远程…

【人工智能】用Python迈向轻量化深度学习——模型压缩与量化实战指南

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着移动端与嵌入式设备对深度学习推理需求的不断提升,模型的体积和推理效率成为关注重点。本文系统介绍了模型压缩与量化技术,详细阐述了…

Linux内核,slub分配流程

我们根据上面的流程图&#xff0c;依次看下slub是如何分配的 首先从kmem_cache_cpu中分配&#xff0c;如果没有则从kmem_cache_cpu的partial链表分配&#xff0c;如果还没有则从kmem_cache_node中分配&#xff0c;如果kmem_cache_node中也没有&#xff0c;则需要向伙伴系统申请…

使用pyinstaller对gradio和chromadb进行打包

解决gradio和chromadb的打包问题 背景问题gradio和gradio_client模块chromadb模块 解决 背景 python项目里包含了gradio和chromadb模块&#xff0c;使用pyinstaller后总有模块找不到&#xff0c;这里分享一个办法一招解决。 问题 gradio和gradio_client模块 gradio在被打进…

【Redis】基础知识入门

文章目录 Redis 入门SQL && NoSQLRedis 介绍 Redis 常见命令Redis数据结构介绍通用命令String 类型基本内容介绍常见的命令key 的结构 Hash类型基本内容介绍常见命令 List类型基本内容介绍常见命令 Set类型基本内容介绍常见命令 SortedSet 类型基本内容介绍常见命令 Re…