ROS2底层机制源码分析
-
init
->init_and_remove_ros_arguments
->init
->Context::init 保存初始化传入的信号
->install_signal_handlers→SignalHandler::install 开线程响应信号
->_remove_ros_arguments 移除ros参数
->SingleNodeManager::instance().init
->mogo_recorder::MogoRecorder::instance().Init 中间件录包初始化
->创建全局静态的NodeHandle
-
NodeHandle
-
构造nodehandle,并校验命名空间,内部调用
voidNodeHandle::construct(){SingleNodeManager::instance().increase_ref();} - param->从redis获取配置信息
-
-
create_generic_subscription 订阅接口
-->topics_interface->add_subscription(subscription, options.callback_group);-->voidCallbackGroup::add_subscription(constrclcpp::SubscriptionBase::SharedPtr subscription_ptr) {std::lock_guard<std::mutex> lock(mutex_);subscription_ptrs_.push_back(subscription_ptr);// timer service client waitable一样的逻辑subscription_ptrs_.erase(std::remove_if(subscription_ptrs_.begin(),subscription_ptrs_.end(),[](rclcpp::SubscriptionBase::WeakPtr x) {returnx.expired();}),subscription_ptrs_.end());} -
mogo::AsyncSpinner
/**
* AsyncSpinner 用来异步spin 某一个 callback group
* 如果thread_num =1,将创建一个独立的线程,进行指定callback group的spin
* 如果thread_num >1,将使用multithead spinner 执行callback group的spin
*
* 提示:若只是想要进行整个node的多线程的spin,请使用 mogo::multithread_spin
*/-
构造
-
start
创建执行器,将构造中传入的callback_group对象传入执行器,传入node;单独开个线程spin;多线程spin就是开指定多个线程并行spin动作
voidAsyncSpinner::start(){std::lock_guard<std::mutex> lock(mutex_);if(is_started_) {return;}mogo::spin();if(thread_num_ ==1) {exec_ = rclcpp::executors::SingleThreadedExecutor::make_shared();}else{exec_ = rclcpp::executors::MultiThreadedExecutor::make_shared(rclcpp::ExecutorOptions(), thread_num_);}exec_->add_callback_group(callback_group_,SingleNodeManager::instance().get_node()->get_node_base_interface());// 将回调组跟节点对象存入mapth_ = std::thread([this] {longtid = syscall(SYS_gettid);if(tid >0) {this->th_id_.store((int)tid);}exec_->spin();});is_started_ =true;}voidMultiThreadedExecutor::spin(){if(spinning.exchange(true)) {throwstd::runtime_error("spin() called while already spinning");}RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );std::vector<std::thread> threads;size_t thread_id =0;{std::lock_guard wait_lock{wait_mutex_};for(; thread_id < number_of_threads_ -1; ++thread_id) {auto func = std::bind(&MultiThreadedExecutor::run,this, thread_id);threads.emplace_back(func);}}run(thread_id);for(auto & thread : threads) {thread.join();}}voidSingleThreadedExecutor::spin(){if(spinning.exchange(true)) {throwstd::runtime_error("spin() called while already spinning");}RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );while(rclcpp::ok(this->context_) && spinning.load()) {rclcpp::AnyExecutable any_executable;if(get_next_executable(any_executable)) {// 内部从map中取execute_any_executable(any_executable);}}}
-
-
mogo::spin
voidspin(){SingleNodeManager::instance().spin();}voidSingleNodeManager::spin(){if(MOGO_UNLIKELY(!is_start_)) {throwstd::runtime_error("SingleNodeManager is not running, please create NodeHandle before that!");}if(!is_join_exec_) {std::lock_guard<std::mutex> lock(exec_mutex_);if(!is_join_exec_) {exec_->add_node(node_ptr_);is_join_single_exec_ =true;is_join_exec_ =true;}else{if(!is_join_single_exec_) {throwstd::runtime_error("Node has been joined in another exec");}}}exec_->spin();{std::lock_guard<std::mutex> lock(exec_mutex_);exec_->remove_node(node_ptr_);is_join_single_exec_ =false;is_join_exec_ =false;}}// 以subscription为例,以下详细函数调用栈1. 获取可执行对象boolExecutor::get_next_ready_executable_from_map(AnyExecutable & any_executable,constrclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &weak_groups_to_nodes){TRACEPOINT(rclcpp_executor_get_next_ready);bool success =false;std::lock_guard<std::mutex> guard{mutex_};// Check the timers to see if there are any that are readymemory_strategy_->get_next_timer(any_executable, weak_groups_to_nodes);if(any_executable.timer) {success =true;}if(!success) {// Check the subscriptions to see if there are any that are readymemory_strategy_->get_next_subscription(any_executable, weak_groups_to_nodes);if(any_executable.subscription) {success =true;}}...voidget_next_subscription(rclcpp::AnyExecutable & any_exec,constWeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override{auto it = subscription_handles_.begin();while(it != subscription_handles_.end()) {auto subscription = get_subscription_by_handle(*it, weak_groups_to_nodes);if(subscription) {// Find the group for this handle and see if it can be servicedauto group = get_group_by_subscription(subscription, weak_groups_to_nodes);if(!group) {// Group was not found, meaning the subscription is not valid...// Remove it from the ready list and continue lookingit = subscription_handles_.erase(it);continue;}if(!group->can_be_taken_from().load()) {// Group is mutually exclusive and is being used, so skip it for now// Leave it to be checked next time, but continue searching++it;continue;}// Otherwise it is safe to set and return the any_execany_exec.subscription = subscription;any_exec.callback_group = group;any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);subscription_handles_.erase(it);return;}// Else, the subscription is no longer valid, remove it and continueit = subscription_handles_.erase(it);}}...rclcpp::SubscriptionBase::SharedPtrMemoryStrategy::get_subscription_by_handle(conststd::shared_ptr<constrcl_subscription_t> & subscriber_handle,constWeakCallbackGroupsToNodesMap & weak_groups_to_nodes){for(constauto & pair : weak_groups_to_nodes) {auto group = pair.first.lock();if(!group) {continue;}// check传入的subscriber_handle跟之前创建的是否匹配auto match_subscription = group->find_subscription_ptrs_if([&subscriber_handle](constrclcpp::SubscriptionBase::SharedPtr & subscription) -> bool {returnsubscription->get_subscription_handle() == subscriber_handle;});if(match_subscription) {returnmatch_subscription;}}returnnullptr;}...template<typename Function>rclcpp::SubscriptionBase::SharedPtrfind_subscription_ptrs_if(Function func)const{return_find_ptrs_if_impl<rclcpp::SubscriptionBase, Function>(func, subscription_ptrs_);}template<typename TypeT, typename Function>typename TypeT::SharedPtr _find_ptrs_if_impl(Function func,conststd::vector<typename TypeT::WeakPtr> & vect_ptrs)const{std::lock_guard<std::mutex> lock(mutex_);for(auto & weak_ptr : vect_ptrs) {auto ref_ptr = weak_ptr.lock();if(ref_ptr && func(ref_ptr)) {returnref_ptr;}}returntypename TypeT::SharedPtr();}至此就能匹配到对应的timer service client waitable subscription2. 构造执行器auto it = subscription_handles_.begin();while(it != subscription_handles_.end()) {auto subscription = get_subscription_by_handle(*it, weak_groups_to_nodes);if(subscription) {// Find the group for this handle and see if it can be servicedauto group = get_group_by_subscription(subscription, weak_groups_to_nodes);if(!group) {// Group was not found, meaning the subscription is not valid...// Remove it from the ready list and continue lookingit = subscription_handles_.erase(it);continue;}if(!group->can_be_taken_from().load()) {// Group is mutually exclusive and is being used, so skip it for now// Leave it to be checked next time, but continue searching++it;continue;}// Otherwise it is safe to set and return the any_execany_exec.subscription = subscription;any_exec.callback_group = group;any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);subscription_handles_.erase(it);return;}// Else, the subscription is no longer valid, remove it and continueit = subscription_handles_.erase(it);}3. 执行voidExecutor::execute_any_executable(AnyExecutable & any_exec){if(!spinning.load()) {return;}if(any_exec.timer) {TRACEPOINT(rclcpp_executor_execute,static_cast<constvoid*>(any_exec.timer->get_timer_handle().get()));execute_timer(any_exec.timer);}if(any_exec.subscription) {TRACEPOINT(rclcpp_executor_execute,static_cast<constvoid*>(any_exec.subscription->get_subscription_handle().get()));execute_subscription(any_exec.subscription);}if(any_exec.service) {execute_service(any_exec.service);}if(any_exec.client) {execute_client(any_exec.client);}if(any_exec.waitable) {any_exec.waitable->execute(any_exec.data);}// Reset the callback_group, regardless of typeany_exec.callback_group->can_be_taken_from().store(true);// Wake the wait, because it may need to be recalculated or work that// was previously blocked is now available.try{interrupt_guard_condition_.trigger();}catch(constrclcpp::exceptions::RCLError & ex) {throwstd::runtime_error(std::string("Failed to trigger guard condition from execute_any_executable: ") + ex.what());}}callback如何传入?业务代码订阅subscription = node_handle_.get_node()->create_generic_subscription(topic_meta.name,topic_meta.type,rosbag2_transport::Rosbag2QoS(queue_size),[this, topic_meta](std::shared_ptr<mogo::SerializedMessage> message) {// TODO 超过Xs没有回调加事件上报if(!mogo::ok())return;count_++;staticdoublenow_timestamp = mogo::TimeHelper::to_sec(mogo::Time::now());// calc hz every secondif(mogo::TimeHelper::to_sec(mogo::Time::now()) - now_timestamp >= mogo::TimeHelper::to_sec(mogo::Time::create(1))) {MOGO_INFO_STREAM_THROTTLE(10,"current callback frequency: "<< count_);count_ =0;now_timestamp = mogo::TimeHelper::to_sec(mogo::Time::now());}pushQueue(OutgoingMessage(message, topic_meta.name, topic_meta.type, mogo::Time::now()));},subscription_options);内层调用注册callbacktemplate<typename AllocatorT = std::allocator<void>>GenericSubscription(rclcpp::node_interfaces::NodeBaseInterface * node_base,conststd::shared_ptr<rcpputils::SharedLibrary> ts_lib,conststd::string & topic_name,conststd::string & topic_type,constrclcpp::QoS & qos,// TODO(nnmm): Add variant for callback with message info. See issue #1604.std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,constrclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options): SubscriptionBase(node_base,*rclcpp::get_typesupport_handle(topic_type,"rosidl_typesupport_cpp", *ts_lib),topic_name,options.template to_rcl_subscription_options<rclcpp::SerializedMessage>(qos),true),callback_(callback),ts_lib_(ts_lib){// This is unfortunately duplicated with the code in subscription.hpp.// TODO(nnmm): Deduplicate by moving this into SubscriptionBase.if(options.event_callbacks.deadline_callback) {this->add_event_handler(options.event_callbacks.deadline_callback,RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);}if(options.event_callbacks.liveliness_callback) {this->add_event_handler(options.event_callbacks.liveliness_callback,RCL_SUBSCRIPTION_LIVELINESS_CHANGED);}if(options.event_callbacks.incompatible_qos_callback) {this->add_event_handler(options.event_callbacks.incompatible_qos_callback,RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);}elseif(options.use_default_callbacks) {// Register default callback when not specifiedtry{this->add_event_handler([this](QOSRequestedIncompatibleQoSInfo & info) {this->default_incompatible_qos_callback(info);},RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);}catch(UnsupportedEventTypeException &/*exc*/) {// pass}}if(options.event_callbacks.message_lost_callback) {this->add_event_handler(options.event_callbacks.message_lost_callback,RCL_SUBSCRIPTION_MESSAGE_LOST);}}处理消息voidGenericSubscription::handle_serialized_message(conststd::shared_ptr<rclcpp::SerializedMessage> & message,constrclcpp::MessageInfo &){callback_(message);}消息从哪里来?---boolSubscriptionBase::take_serialized(rclcpp::SerializedMessage & message_out,rclcpp::MessageInfo & message_info_out){rcl_ret_t ret = rcl_take_serialized_message(this->get_subscription_handle().get(),&message_out.get_rcl_serialized_message(),&message_info_out.get_rmw_message_info(),nullptr);if(RCL_RET_SUBSCRIPTION_TAKE_FAILED == ret) {returnfalse;}elseif(RCL_RET_OK != ret) {rclcpp::exceptions::throw_from_rcl_error(ret);}returntrue;}rcl_ret_trcl_take_serialized_message(constrcl_subscription_t * subscription,rcl_serialized_message_t * serialized_message,rmw_message_info_t * message_info,rmw_subscription_allocation_t * allocation){RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,"Subscription taking serialized message");if(!rcl_subscription_is_valid(subscription)) {returnRCL_RET_SUBSCRIPTION_INVALID;// error already set}RCL_CHECK_ARGUMENT_FOR_NULL(serialized_message, RCL_RET_INVALID_ARGUMENT);// If message_info is NULL, use a place holder which can be discarded.rmw_message_info_t dummy_message_info;rmw_message_info_t * message_info_local = message_info ? message_info : &dummy_message_info;*message_info_local = rmw_get_zero_initialized_message_info();// Call rmw_take_with_info.bool taken =false;rmw_ret_t ret = rmw_take_serialized_message_with_info(subscription->impl->rmw_handle, serialized_message, &taken, message_info_local, allocation);if(ret != RMW_RET_OK) {RCL_SET_ERROR_MSG(rmw_get_error_string().str);returnrcl_convert_rmw_ret_to_rcl_ret(ret);}RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,"Subscription serialized take succeeded: %s", taken ?"true":"false");if(!taken) {returnRCL_RET_SUBSCRIPTION_TAKE_FAILED;}returnRCL_RET_OK;}注意:这里已经到rmw层了(DDS的封装层)rmw_ret_trmw_take_serialized_message_with_info(constrmw_subscription_t * subscription,rmw_serialized_message_t * serialized_message,bool * taken,rmw_message_info_t * message_info,rmw_subscription_allocation_t * allocation){returnrmw_fastrtps_shared_cpp::__rmw_take_serialized_message_with_info(eprosima_fastrtps_identifier, subscription, serialized_message, taken, message_info,allocation);}核心代码---循环通过data_reader_->take(data_values, info_seq,1)获取数据,最终内存拷贝到serialized_message中带出rmw_ret_t_take_serialized_message(constchar* identifier,constrmw_subscription_t * subscription,rmw_serialized_message_t * serialized_message,bool * taken,rmw_message_info_t * message_info,rmw_subscription_allocation_t * allocation){(void) allocation;*taken =false;RMW_CHECK_TYPE_IDENTIFIERS_MATCH(subscription handle,subscription->implementation_identifier, identifier,returnRMW_RET_INCORRECT_RMW_IMPLEMENTATION)auto info = static_cast<CustomSubscriberInfo *>(subscription->data);RCUTILS_CHECK_FOR_NULL_WITH_MSG(info,"custom subscriber info is null",returnRMW_RET_ERROR);eprosima::fastcdr::FastBuffer buffer;eprosima::fastdds::dds::SampleInfo sinfo;rmw_fastrtps_shared_cpp::SerializedData data;data.is_cdr_buffer =true;data.data = &buffer;data.impl = nullptr;// not used when is_cdr_buffer is trueeprosima::fastdds::dds::StackAllocatedSequence<void*,1> data_values;const_cast<void**>(data_values.buffer())[0] = &data;eprosima::fastdds::dds::SampleInfoSeq info_seq{1};while(ReturnCode_t::RETCODE_OK == info->data_reader_->take(data_values, info_seq,1)) {auto reset = rcpputils::make_scope_exit([&](){data_values.length(0);info_seq.length(0);});if(info_seq[0].valid_data) {auto buffer_size = static_cast<size_t>(buffer.getBufferSize());if(serialized_message->buffer_capacity < buffer_size) {auto ret = rmw_serialized_message_resize(serialized_message, buffer_size);if(ret != RMW_RET_OK) {returnret;// Error message already set}}serialized_message->buffer_length = buffer_size;memcpy(serialized_message->buffer, buffer.getBuffer(), serialized_message->buffer_length);if(message_info) {_assign_message_info(identifier, message_info, &info_seq[0]);}*taken =true;break;}}returnRMW_RET_OK;}fastrtps-fastdds---查数据ReturnCode_t DataReaderImpl::read_or_take(LoanableCollection& data_values,SampleInfoSeq& sample_infos,int32_t max_samples,constInstanceHandle_t& handle,SampleStateMask sample_states,ViewStateMask view_states,InstanceStateMask instance_states,bool exact_instance,bool single_instance,bool should_take){if(reader_ == nullptr){returnReturnCode_t::RETCODE_NOT_ENABLED;}ReturnCode_t code = check_collection_preconditions_and_calc_max_samples(data_values, sample_infos, max_samples);if(!code){returncode;}#ifHAVE_STRICT_REALTIMEauto max_blocking_time = std::chrono::steady_clock::now() +std::chrono::microseconds(::TimeConv::Time_t2MicroSecondsInt64(qos_.reliability().max_blocking_time));std::unique_lock<RecursiveTimedMutex> lock(reader_->getMutex(), std::defer_lock);if(!lock.try_lock_until(max_blocking_time)){returnReturnCode_t::RETCODE_TIMEOUT;}#elsestd::lock_guard<RecursiveTimedMutex> _(reader_->getMutex());#endif// if HAVE_STRICT_REALTIMEset_read_communication_status(false);auto it = history_.lookup_available_instance(handle, exact_instance);if(!it.first){if(exact_instance && !history_.is_instance_present(handle)){returnReturnCode_t::RETCODE_BAD_PARAMETER;}else{returnReturnCode_t::RETCODE_NO_DATA;}}code = prepare_loan(data_values, sample_infos, max_samples);if(!code){returncode;}detail::StateFilter states{ sample_states, view_states, instance_states };detail::ReadTakeCommand cmd(*this, data_values, sample_infos, max_samples, states, it.second, single_instance);while(!cmd.is_finished()){cmd.add_instance(should_take);}returncmd.return_value();} -
create_publisher
-
publish

-
mogo::shutdown
bool shutdown(){returnrclcpp::shutdown();}
相关文章:
ROS2底层机制源码分析
init ->init_and_remove_ros_arguments ->init ->Context::init 保存初始化传入的信号 ->install_signal_handlers→SignalHandler::install 开线程响应信号 ->_remove_ros_arguments 移除ros参数 ->SingleNodeManager::instance().…...
超越 Transformer开启高效开放语言模型的新篇章
在人工智能快速发展的今天,对于高效且性能卓越的语言模型的追求,促使谷歌DeepMind团队开发出了RecurrentGemma这一突破性模型。这款新型模型在论文《RecurrentGemma:超越Transformers的高效开放语言模型》中得到了详细介绍,它通过…...
快速排序-Hoare 递归版 C语言
个人主页点这里~ 快速排序的简介: 快速排序是Hoare于1962年提出的一种 二叉树结构 的 交换 排序方法,其基本思想为:任取待排序元素序列中 的某元素作为 基准值 ,按照该排序码将待排序集合分割成 两子序列 , 左子序列中所有元素均 …...
C语言经典指针运算笔试题图文解析
指针运算常常出现在面试题中,画图解决是最好的办法。 题目1: #include <stdio.h> int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d", *(a 1), *(ptr - 1));return 0; } //程序的结果是什么&…...
使用 KubeKey v3.1.1 离线部署原生 Kubernetes v1.28.8 实战
今天,我将为大家实战演示,如何基于操作系统 openEuler 22.03 LTS SP3,利用 KubeKey 制作 Kubernetes 离线安装包,并实战离线部署 Kubernetes v1.28.8 集群。 实战服务器配置 (架构 1:1 复刻小规模生产环境,配置略有不…...
DOS 命令
Dos: Disk Operating System 磁盘操作系统, 简单说一下 windows 的目录结构。 ..\ 到上一级目录 常用的dos 命令: 查看当前目录是有什么内容 dir dir d:\abc2\test200切换到其他盘下:盘符号 cd : change directory 案例演示:切换…...
如何用Java程序实现一个简单的消息队列?
在Java程序中,可以使用内置的java.util.concurrent.BlockingQueue作为消息队列存放的容器,来实现一个简单的消息队列。 具体实现如下,在这个例子中,我们创建了一个生产者线程和一个消费者线程,他们共享同一个阻塞队列…...
OpenAI 宕机事件:GPT 停摆的影响与应对
引言 2024年6月4日,OpenAI 的 GPT 模型发生了一次全球性的宕机,持续时间长达8小时。此次宕机不仅影响了OpenAI自家的服务,还导致大量用户涌向竞争对手平台,如Claude和Gemini,结果也导致这些平台出现故障。这次事件的广…...
linux常用的基础命令
ls - 列出目录内容。 cd - 更改目录。 pwd - 打印当前工作目录。 mkdir - 创建新目录。 rmdir - 删除空目录。 touch - 创建新文件或更新现有文件的时间戳。 cp - 复制文件或目录。 mv - 移动或重命名文件或目录。 rm - 删除文件或目录。 cat - 显示文件内容。 more - 分页显示…...
618家用智能投影仪推荐:这个高性价比品牌不容错过
随着科技的不断进步,家庭影院的概念已经从传统的大屏幕电视逐渐转向了更为灵活和便携的家用智能投影仪。随着618电商大促的到来,想要购买投影仪的用户们也开始摩拳擦掌了。本文将从投影仪的基础知识入手,为您推荐几款性价比很高的投影仪&…...
自愿离婚协议书
自愿离婚协议书 男方(夫): 女方(妻): 双方现因 原因,导致夫妻情感已破裂,自愿离婚…...
WPS JSA 宏脚本入门和样例
1入门 WPS window版本才支持JSA宏的功能。 可以自动化的操作文档中的一些内容。 参考文档: WPS API 参考文档:https://open.wps.cn/previous/docs/client/wpsLoad 微软的Word API文档:Microsoft.Office.Interop.Word 命名空间 | Microsoft …...
Printing and Exporting
打印 大多数DevExpress。NET控件(XtraGrid、XtraPivotGrid、XttraTreeList、XtraScheduler、XtraCharts)提供打印和导出功能。 所有可打印的DevExpress.NET控件是使用XtraPrinting库提供的方法打印的。 若要确定预览和打印选项是否可用,请检…...
c++【入门】正多边形每个内角的度数
限制 时间限制 : 1 秒 内存限制 : 128 MB 题目 根据多边形内角和定理,正多边形内角和等于:(n - 2)180(n大于等于3且n为整数)(如下图所示是三角形、四边形、五边形、六边形的形状)…...
spring boot3登录开发-邮箱登录/注册接口实现
⛰️个人主页: 蒾酒 🔥系列专栏:《spring boot实战》 🌊山高路远,行路漫漫,终有归途 目录 写在前面 上文衔接 内容简介 功能分析 所需依赖 邮箱验证登录/注册实现 1.创建交互对象 2.登录注册业务逻辑实…...
数据结构-二叉搜索树
二叉搜索树:BST(Binary Search Tree) 二叉搜索树是二叉树,可以为空,如果不为空,满足以下性质: 非空左子树的所有键值小于其根节点的键值非空右子树的所有键值大于其根节点的键值左、右字数本身也都是二叉搜索树 二叉…...
JUnit:Java开发者不可或缺的单元测试框架
在软件开发过程中,测试是确保代码质量的关键环节。单元测试作为测试体系的基础,对提升代码质量、降低bug率、增强软件稳定性具有重要作用。JUnit 作为 Java 语言事实上的标准单元测试框架,已经成为 Java 开发者进行单元测试的首选工具。本文将…...
NG32单片机GPIO口配置方式
目录 一、引言 二、GPIO口基本结构 三、GPIO口配置方式 四、工作原理 五、总结 一、引言 NG32单片机是一款集成度高、功能强大的微控制器。其中,GPIO(General Purpose Input/Output)口作为单片机与外部设备通信的重要接口,具…...
SpringCloud-OpenFeign拓展-连接池、最佳使用方法、日志输出
目录 1 OpenFeign连接池 1.1 常见连接类型 1.2 连接池使用方法 1.2.1 引入依赖 1.2.2 开启连接池功能 1.2.3 配置完成,重启实例即可,底层将更改设置。 2 OpenFeign最佳使用方法 2.1 每个微服务都是单独的project,内部有三个独立模块 …...
跨链协议中Cosmos IBC、Polkadot/XCM、Celer Network的区别以及用途
跨链协议是实现不同区块链之间通信和价值转移的关键技术。Cosmos IBC、Polkadot/XCM 和 Celer Network 是三个在跨链领域内具有代表性的协议,它们各自有着独特的设计理念和应用场景。下面是这三个协议的详细对比: Cosmos IBC (Inter-Blockchain Communi…...
从AHB到AXI:手把手带你用Verilog仿真看Outstanding如何提升SoC数据吞吐
从AHB到AXI:深入解析Outstanding机制如何优化SoC数据吞吐效率 在复杂的SoC设计中,总线架构的选择直接影响系统性能。传统AHB总线虽然结构简单,但在高并发场景下容易成为瓶颈。AXI协议通过引入Outstanding、Out-of-order等机制,显著…...
Qwen3.5-4B-Claude-Opus部署教程:CSDN镜像Web服务7860端口配置详解
Qwen3.5-4B-Claude-Opus部署教程:CSDN镜像Web服务7860端口配置详解 1. 模型概述 Qwen3.5-4B-Claude-4.6-Opus-Reasoning-Distilled-GGUF是一个基于Qwen3.5-4B的推理蒸馏模型,特别强化了结构化分析、分步骤回答以及代码与逻辑类问题的处理能力。该版本以…...
FPGA实战:3级CIC滤波器Verilog实现与仿真(附完整代码)
FPGA实战:3级CIC滤波器Verilog实现与仿真全解析 在数字信号处理领域,CIC(Cascaded Integrator-Comb)滤波器因其结构简单、运算高效的特点,成为多速率系统中的关键组件。本文将深入探讨3级CIC滤波器的Verilog实现细节&a…...
手把手教你学Simulink——基于Simulink的同步整流Buck变换器效率提升仿真
目录 手把手教你学Simulink——基于Simulink的同步整流Buck变换器效率提升仿真 摘要 一、背景与挑战 1.1 传统二极管整流的效率瓶颈 1.1.1 二极管损耗机理 1.2 同步整流的优势与挑战 1.2.1 同步整流原理 1.2.2 核心挑战 1.3 设计目标 二、系统架构与…...
Flutter 实现点击任意位置收起键盘的最佳实践
痛点 在 Flutter 开发中,TextField 聚焦后会弹出键盘,关闭键盘通常需要: 点击系统返回键点击输入框外的空白区域(但很多情况下点击空白区域也没反应)点击其他输入框(键盘会切换到另一个输入框,不…...
bert-base-chinese场景解析:从语义相似度计算到特征提取实战
BERT-base-chinese场景解析:从语义相似度计算到特征提取实战 1. 模型概述与核心价值 BERT-base-chinese是Google推出的中文预训练语言模型,基于Transformer架构构建,专门针对中文文本处理进行了优化。作为NLP领域的里程碑式模型,…...
电脑c盘变红了怎么清理?C盘清理工具与方法
电脑c盘变红了怎么清理?问题不难解决,关键是选对方法工具!下面介绍实用的清理C盘方法,便于你解决C盘变红的问题哦! 关于C盘清理工具,给大家安排一款针对C盘爆满的清理神器---Windows - Cleaner,…...
禅修运维法:服务器宕机时集体冥想
当技术危机遇上心灵平静在软件测试领域,服务器宕机是高频挑战,不仅中断测试流程,还引发团队压力。传统运维强调硬件修复和代码调试,但忽略了人的因素——压力下的决策失误往往加剧问题。禅修运维法创新性地将佛教禅修融入IT管理&a…...
CosyVoice语音克隆应用案例:为短视频配音、制作个性化语音问候消息
CosyVoice语音克隆应用案例:为短视频配音、制作个性化语音问候消息 最近帮朋友做短视频账号,发现一个挺头疼的问题:每次拍完视频,找配音特别麻烦。要么自己录,口音重还费时间;要么用AI配音,声音…...
s2-pro语音合成教程:参考音频采样率/格式/信噪比最佳实践
s2-pro语音合成教程:参考音频采样率/格式/信噪比最佳实践 1. 认识s2-pro语音合成工具 s2-pro是Fish Audio开源的专业级语音合成模型镜像,它不仅能将文本转换为自然流畅的语音,还能通过参考音频来复用特定的音色。这意味着你可以上传一段样本…...
