【智体OS】官方上新发布智体机器人:使用rtrobot智体应用远程控制平衡车机器人
【智体OS】官方上新发布智体机器人:使用rtrobot智体应用远程控制平衡车机器人
dtns.network是一款主要由JavaScript编写的智体世界引擎(内嵌了three.js编辑器的定制版-支持以第一视角浏览3D场馆),可以在浏览器和node.js、deno、electron上运行,它是一个跨平台的软件,支持多个操作系统使用!
dtns.connector是dtns.network的客户端软件,允许多用户方便自由地连接dtns.network的智体设备。支持使用内置的poplang智体编程语言实现3D组件的智能化编程——语法超简单,一句话语法,人人轻松上手!通过poplang智体编程,可轻松创建、编辑、分发xverse-3D智体应用。
本次上新的主要内容为:使用rtrobot智体应用实时视频远程控制智体机器人(使用千元内的平衡车底盘),可实现全球互联网实时控制远程机器人(物流配送、导盲机器人、巡检机器人、安保机器人、旅游陪伴、家庭看护机器人等)
更新内容
1、使用dpkg机制,集成和拓展dtns-rtphone-api,开发了rtrobot分布式远程访问和实时视频控制的DPKG机器人智体应用rtrobot3.0.dpkg(dtns.top官网下载)
2、使用joystick控制球实现机器人的任意方向、任意角度的远程控制(前后左右任意角度和方向)
3、可方便集成到了dtns.os的系统应用面板中。
4、可使用poplang调用dtns-rtpc-api,实现控制机器人的任意功能插件(用户自定义)
5、可在手机上安装dtns.connector智体OS的客户端,打开rtrobot3.0dpkg,实现机器人的远程访问和实时视频控制。
完全开源:rtrobot智体机器人控制应用、dtns.os和dtns.network等项目均开源。详见文末、或访问dtns.top智体OS官网。
使用教程
一、打开dtns.connector的dweb头榜界面,先下载和初始化opencv-js(用于适配平衡车机器人底盘的蓝牙控制器应用)
打开该opencv-js应用后,如下图所示:
显示dpkg插件加载成功。
二、打开dtns.connector的dweb头榜界面,点击上传了的rtrobo3.3.dpkg(或任意其它最新版本)
三、进入rtrobot远程访问和实时视频控制的机器人智体应用,可看到完成初始化操作(使用opencv-js识别蓝牙控制器应用的滚动球中心点)
注:使用Opencv-js自动识别滚动球中心点坐标,方便后续的dtns-rtphone-api调用adb shell指令完成相应的任意角度的移动指令。
四、加载成功实时视频如下(需在平衡车机器人上安放一台手机,以使用rtchat调用摄像头功能)
注:顶部是RT机器人(客户端)标题和左右两侧的功能区(返回和poplang功能-插件),中间部分是实时视频,底部中间位置是滚动球控制器joystick,可实现智体机器人任意角度和方向的移动。
五、使用滚动球控制方向移动平衡车机器人
注:向左前方移动(如上图所示)
六、拍摄的平衡车机器人相片(如下图)
注:底部是平衡车机器人底盘,使用一台安卓手机打开dtns.connector的智体IB中的视频聊天,输入roomid为rtrobot,以实现机器人实时视野到rtrobot智体应用上,方便实时的画面传输和视频远程控制。
rtrobot3.0.dpkg的源码分享
<!--* @Description: RtRobot机器人控制端(用户端)* @Author: poplang* @Date: 2024-12-12* @LastEditors: * @LastEditTime:--><template><div style="width: 100%;height: 100%;padding:0px;margin: 0px;background-repeat: no-repeat;background-size: cover;" ref="rtvideoroombody"><div @click="back" style="color:black;position: fixed;left:8px;top:8px;z-index: 399;"> ❮返回 </div><div style="color:black;position: fixed;left:0;right:0;top:8px;z-index: 359;text-align: center; font-size: 18px;font-weight: 800;">{{ title }}</div><div style="color:black;position: fixed;right:8px;top:8px;z-index: 399;"><!-- <span @click="showTouchPadFlag=!showTouchPadFlag" style="margin-right: 8px">{{ showTouchPadFlag?'触屏':'画面' }}</span><span @click="syncScreen" style="margin-right: 8px">{{ syncScreenTips }}</span> --><span @click="showFlag=true" style="margin-right: 8px">功能</span><!-- <span @click="queryScreen">刷新</span> --></div><div id="rtrobot_container" style="position:fixed;top: 45px;bottom: 0px;left: 0px;right: 0px;z-index: 9;overflow: hidden;background:#505050;-moz-user-select:none;-webkit-user-select:none; -ms-user-select:none; -khtml-user-select:none;user-select:none;"><RtVideo ref="rtvideo":creator="creator":roomId="room_id":enableLogs="true":enableVideo="enableAudio":enableAudio="enableAudio":socketURL = "socketURL":cameraHeight="100"style="width: 100%;height: 100%;-moz-user-select:none;-webkit-user-select:none; -ms-user-select:none; -khtml-user-select:none;user-select:none;overflow: hidden;"/></div><van-popup v-model="showFlag" position="top" :style="{ height: '35%' }" ><van-grid><van-grid-item @click="call(item)" v-for="(item,index) in list" :key="index" icon="photo-o" :text="item.title"></van-grid-item></van-grid></van-popup></div></template><script>import RtVideo from './RtVideo.vue'export default {name: "RtRobotClient",props: ["value"],components: { RtVideo },data() {return {title:'RT机器人(客户端)',enableAudio:false,socketURL:window.g_rtchat_tns_url ? window.g_rtchat_tns_url : "https://groupbuying.opencom.cn:441",//"http://192.168.2.102:3000","http://127.0.0.1:3000",//room_id:'rtrobot',creator:false,showFlag:false,list:[],centerX:0,centerY:0,areaWidth:300,lastPos:{x:0,y:0},nowPos:{x:0,y:0},quitFlag:false,stopFlag:true,}},async created(){this.user_id = localStorage.user_id},async mounted(){// console.log('rtvideoroomimg:',rtvideoroomimg,this.h)// this.$refs.rtvideoroombody.style.backgroundImage = `url(${rtvideoroomimg})`window.g_rtvideoroombody = this.$refs.rtvideoroombodythis.init()await this.centerPosition()this.doRun()},beforeRouteLeave(to,from,next){console.log('beforeRouteLeave-to-from:',to,from)if(to.path != from.path){console.log('into beforeRouteLeave')this.unbindEvent()next();}},methods: {back(){this.$router.go(-1)this.unbindEvent()},async centerPosition(){if(!window.g_dtnsManager) return let ret = await g_dtnsManager.run('dtns://web3:'+window.rpc_client.roomid+'/rtphone/robot/screencap')if(!ret || !ret.ret) return this.$toast('获取截图失败!原因:'+(ret?ret.ret:'未知网络原因'))if(typeof g_opencv_image2circles != 'function') return this.$toast('g_opencv_image2circles函数不存在,请先加载opencv-js插件!')let circlesRet = await g_opencv_image2circles(ret.base64,50,60)if(!circlesRet || !circlesRet.circles || circlesRet.circles.length<=0) return this.$toast('识别中心点失败!')let circles = circlesRet.circlesthis.areaWidth = (circlesRet.w /2 )- 100this.centerX = circles[circles.length-1].xthis.centerY = circles[circles.length-1].yconsole.log('center-x-y:',this.centerX,this.centerY,this.areaWidth,circlesRet)this.$toast('识别中心点成功!'+this.centerX+','+this.centerY)this.lastPos.x = this.centerXthis.lastPos.y = this.centerY},async doRun(){while(!this.quitFlag){await new Promise((res)=>setTimeout(res,500))if(this.stopFlag){continue}else //发送do-action-event-stream{if(this.nowPos.x == this.lastPos.x && this.nowPos.y == this.lastPos.y){this.lastPos.x = this.nowPos.x - 1this.lastPos.y = this.nowPos.y - 1}let action = 'shell input swipe '+this.lastPos.x+' '+this.lastPos.y+' '+this.nowPos.x+' '+this.nowPos.y+' 500 '//不等待g_dtnsManager.run('dtns://web3:'+window.rpc_client.roomid+'/rtphone/robot/do/timeout',{action,timeout:500}).then((ret)=>{console.log('doRun-ret:',ret,action)})}}},async updatePos(forward,turn){// if(this.doNowFlag) return false// this.doNowFlag = trueif( Math.abs(forward) + Math.abs(turn)<=0.00001){//回到原始中心点。this.lastPos.x = (this.nowPos.x = this.centerX) this.lastPos.y = (this.nowPos.y = this.centerY) this.stopFlag = true}else{this.stopFlag = falsethis.nowPos.x = turn * this.areaWidth + this.centerX this.nowPos.y = forward * this.areaWidth + this.centerY// if(this.nowPos.x == this.lastPos.x && this.nowPos.y == this.lastPos.y)// this.stopFlag = false// else this.stopFlag = true}if(this.nowPos.x == this.lastPos.x && this.nowPos.y == this.lastPos.y){this.lastPos.x = this.nowPos.x - 1this.lastPos.y = this.nowPos.y - 1}let action = 'shell input swipe '+this.lastPos.x+' '+this.lastPos.y+' '+this.nowPos.x+' '+this.nowPos.y+' 1000 'if(false){let ret = await g_dtnsManager.run('dtns://web3:'+window.rpc_client.roomid+'/rtphone/robot/do',{action:'shell input swipe '+this.lastPos.x+' '+this.lastPos.y+' '+this.nowPos.x+' '+this.nowPos.y+' 1000 '})console.log('updatePos-ret:',ret)}else{// let ret = await g_dtnsManager.run('dtns://web3:'+window.rpc_client.roomid+'/rtphone/robot/do/timeout',{action,timeout})// console.log('updatePos-ret:',ret)}this.lastPos =this.nowPos// this.doNowFlag = false},init(){this.bindEvent()// if(!window.g_dtnsManager) return // const focusRet = await window.g_dtnsManager.run('dtns://web3:'+window.rpc_client.roomid+'/rtchannel/focus',{channel:rtvideoroom_channel_name})// if(!focusRet ||!focusRet.ret) return this.$toast('订阅频道失败!原因:'+(focusRet?focusRet.msg:'未知网络原因'))// this.$toast('订阅频道成功')const touchEnabled = !!('ontouchstart' in window);const rtrobotThis = thisclass JoyStick {constructor(options) {this.createDom()this.maxRadius = options.maxRadius || 40this.maxRadiusSquared = this.maxRadius * this.maxRadiusthis.onMove = options.onMovethis.game = options.gamethis.origin = {left: this.domElement.offsetLeft,top: this.domElement.offsetTop}console.log(this.origin)this.rotationDamping = options.rotationDamping || 0.06this.moveDamping = options.moveDamping || 0.01this.createEvent()}createEvent() {const joystick = thisif(touchEnabled) {window.JoyStick_touchstart = function(e) {console.log('touchstart...')// e.preventDefault()joystick.tap(e)// e.stopPropagation()}this.domElement.addEventListener('touchstart', window.JoyStick_touchstart)} else {window.JoyStick_mousedown = function(e) {// e.preventDefault()console.log('mousedown...')joystick.tap(e)// e.stopPropagation()}this.domElement.addEventListener('mousedown',window.JoyStick_mousedown )}}getMousePosition(e) {const clientX = e.targetTouches ? e.targetTouches[0].pageX : e.clientXconst clientY = e.targetTouches ? e.targetTouches[0].pageY : e.clientYreturn {x:clientX, y:clientY}}tap(e) {this.offset = this.getMousePosition(e)const joystick = thisthis.onTouchMoved = function(e) {// e.preventDefault()joystick.move(e)}this.onTouchEnded = function(e) {// e.preventDefault()joystick.up(e)}if(touchEnabled) {document.addEventListener('touchmove', this.onTouchMoved)document.addEventListener('touchend', this.onTouchEnded)} else {document.addEventListener('mousemove', this.onTouchMoved)document.addEventListener('mouseup', this.onTouchEnded)}}move(e) {// if(window.g_3d_editor_stop_player_flag) return const mouse = this.getMousePosition(e)let left = mouse.x - this.offset.xlet top = mouse.y - this.offset.yconst sqMag = left * left + top * topif (sqMag > this.maxRadiusSquared){const magnitude = Math.sqrt(sqMag)left /= magnitudetop /= magnitudeleft *= this.maxRadiustop *= this.maxRadius}this.domElement.style.top = `${ top + this.domElement.clientHeight / 2 }px`this.domElement.style.left = `${ left + this.domElement.clientWidth / 2 }px`const forward = -(top - this.origin.top + this.domElement.clientHeight / 2) / this.maxRadiusconst turn = (left - this.origin.left + this.domElement.clientWidth / 2) / this.maxRadiusif(this.onMove) {this.onMove(forward, turn)}}up() {if (touchEnabled){document.removeEventListener('touchmove', this.onTouchMoved)document.removeEventListener('touchend', this.onTouchEned)}else{document.removeEventListener('mousemove', this.onTouchMoved)document.removeEventListener('mouseup', this.onTouchEned)}this.domElement.style.top = `${this.origin.top}px`this.domElement.style.left = `${this.origin.left}px`if(this.onMove) {this.onMove(0, 0)}}createDom() {const circle = document.createElement('div')circle.style.cssText = `position: absolute;bottom: 35px;width: 80px;height: 80px;background: rgba(126, 126, 126, 0.2);border: #444 solid medium;border-radius: 50%;left: 50%;transform: translateX(-50%);`const thumb = document.createElement('div')thumb.style.cssText = `position: absolute;left: 18px;top: 17px;width: 40px;height: 40px;border-radius: 50%;background: #fff;`circle.appendChild(thumb)// document.body.appendChild(circle)const container = document.querySelector('#rtrobot_container')container.appendChild(circle)this.domElement = thumbthumb.addEventListener('dblclick',function(){console.log('JoyStick-dblclick is clicked!')// window.history.go(-1)})this.circleElement = circlewindow.x3dplayer_joystickCicle = circle} }window.JoyStick_instance = new JoyStick({onMove: function(forward, turn) {forward = -forwardif(Math.abs(forward) < 0.05) forward = 0if(Math.abs(turn) < 0.5) turn = 0// move.forward = forward// move.turn = turnconsole.log('forward-turn:',forward,turn)rtrobotThis.updatePos(forward,turn)}})},bindEvent(){this.onJoin()},async unbindEvent(){this.quitFlag = truethis.onLeave()// document.removeEventListener('keydown', onKeyDown)// document.removeEventListener('keyup', onKeyUp)console.log('removeEvents:',window.JoyStick_instance)const container = document.querySelector('#rtrobot_container')container.removeChild(window.JoyStick_instance.circleElement)document.removeEventListener('touchmove',window.JoyStick_instance.onTouchMoved)document.removeEventListener('touchend',window.JoyStick_instance.onTouchEnded)document.removeEventListener('mousemove',window.JoyStick_instance.onTouchMoved)document.removeEventListener('mouseup',window.JoyStick_instance.onTouchEnded)},onJoin() {this.join_flag = trueif(!this.$refs.rtvideo.signalClient)//localStream){this.$refs.rtvideo.join()}},onLeave() {try{this.join_flag = falsethis.$refs.rtvideo.leave();this.$refs.rtvideo.localStream = nullthis.$refs.rtvideo.screenStream = null//修复更新新局时,旧的未关闭的问题// this.audioCloseFlag = true// this.showAudioStatus(true)//会调用onLeave,故应该等等 this.chessInfo = null之后,方进行设置}catch(ex){console.log('onLeave-exception:'+ex,ex)}}}}</script>
<style scoped>
</style>
注:集成了RtVideo组件和内置的JoyStick滚动球控制器javascript组件。并且使用dtns-api:/rtphone/robot/do/timeout实现了机器人移动的事件流的前后端传输,方便将adb shell指令实时传输至后端。成功实现了rtrobot标准的开源的机器人远程实时视频控制的智体应用。
总结:rtrobot和rtvideo的结合,使得平衡车机器人变成了一个非常易于使用的远程视频实时控制的智体机器人。并且支持在功能拓展区集成poplang智体插件,从而大大提升了智体机器人的用户体验——内容极度丰富、使用超级简单、成本极其低廉。使得大量的机器人应用场景可以快速集成和开发,包含但不限于家用、商用、安防使用、养老、育儿、玩乐、旅游、教育、产业链、物流领域。
相关文章:

【智体OS】官方上新发布智体机器人:使用rtrobot智体应用远程控制平衡车机器人
【智体OS】官方上新发布智体机器人:使用rtrobot智体应用远程控制平衡车机器人 dtns.network是一款主要由JavaScript编写的智体世界引擎(内嵌了three.js编辑器的定制版-支持以第一视角浏览3D场馆),可以在浏览器和node.js、deno、e…...
Blazor(.razor)+VUE+elementUI适合一起用吗
在实际项目中,将 Blazor(.razor) 与 Vue.js 和 ElementUI 一起使用是可以实现的,但是否适合取决于你的项目需求、开发团队的技术栈和具体场景。以下是对这种组合的详细分析: 一、适合一起使用的场景 1.1 逐步引入 Bla…...

SpringBoot左脚进门之Maven管理家
一、概念 Maven 是一个项目管理和整合工具。通过对 目录结构和构建生命周期 的标准化, 使开发团队用极少的时间就能够自动完成工程的基础构建配置。 Maven 简化了工程的构建过程,并对其标准化,提高了重用性。 Maven 本地仓库 (Local Reposi…...

188-下翻便携式6U CPCI工控机箱
一、板卡概述 下翻式CPCI便携工控机,系统采用6u cpci背板结构,1个系统槽,7个扩展槽, 满足对携带的需求,可装标准6U8槽CPCI主板,8个扩展槽, 满足客户对空间扩展的需求.可宽温服务的工作产品,15高亮度液晶显示屏,超薄88键笔记本键盘,触摸式鼠标,加固型机箱结构,使它能够适应各种复…...
Ubuntu 挂载目录
1. 临时挂载(重启后失效) 创建挂载点: $ sudo mkdir -p /work临时挂载磁盘到 work 目录: $ sudo mount /dev/nvme0n1p1 /work验证挂载是否成功: $ df -h /work此方法挂载在系统重启后会失效,需手动重新挂载…...

基于IEEE 802.1Qci的时间敏感网络(TSN)主干架构安全分析及异常检测系统设计
中文标题:基于IEEE 802.1Qci的时间敏感网络(TSN)主干架构安全分析及异常检测系统设计 英文标题:Security Analysis of the TSN Backbone Architecture and Anomaly Detection System Design Based on IEEE 802.1Qci 作者信息&…...

2024年食堂采购系统源码技术趋势:如何开发智能的供应链管理APP
本篇文章,小编将与大家一同探讨2024年食堂采购系统的技术趋势,并提供开发更智能的供应链管理APP的策略。 一、2024年食堂采购系统的技术趋势 1.人工智能与机器学习的深度应用 在2024年,AI和机器学习在食堂采购系统中的应用将更加普遍。这些…...

zotero安装教程(包括茉莉花插件)
zotero安装教程(包括茉莉花插件) zotero下载(windows)1-安装 Zotero2-安装 Zotero Connector3-安装浏览器插件--jasminum茉莉花功能:插件下载地址:[https://github.com/search?qjasminum&typerepositories](https://github.c…...
webpack4 - 配置文件分离(详细教程)
webpack根据开发和生成环境一般可以将配置文件拆分,拆分dev和prod两种环境 |- package.json|- /build|- webpack.base.js|- webpack.dev.js|- webpack.prod.js在scripts里修改相应的命令 "dev": "webpack-dev-server --config build/webpack.dev.j…...
MongoDB 分片
MongoDB 分片 MongoDB 分片是一种数据库架构,用于将大量数据分布存储在多个服务器上。这种设计允许数据库扩展,以处理大量数据和高吞吐量操作。分片通过将数据集分割成小块,称为分片,并将这些分片分布到多个服务器上来工作。每个…...

PHP加载MySQL扩展
PHP本身不具备操作MySQL数据库的能力,需要借助PHP操作MySQL的扩展来实现 1、PHP加载MySQL扩展:php.ini文件中 2、PHP中所有的扩展都在ext文件中,需要指定扩展所在路径:extension_dir 3、php.ini 已经被apache加载,所以…...

期末复习-计算机网络篇SCAU
第一章:概述 1.计算机网络的特点,互联网发展的三个阶段 特点:连通性、资源共享 三个阶段: 1969-1990:从单个网络ARPANET向互联网发展 1985-1993:建成了三级结构的互联网 1993-现在:全球范…...

使用LLM进行股价预测(附代码)
使用LLM进行股价预测(附代码) 注意 代码是完整的,但是需要 https://github.com/wxy2ab/akinterpreter 才能完整运行 利用 Python 和 AkShare 进行股票数据分析与预测:以中远海控为例 在本文中,我们将使用 Python 的 akshare 库获取中远海…...

分支限界笔记
文章目录 概要整体架构流程基本概念分支限界法的定义核心思想 简单问题介绍问题:简单背包问题思考:暴力解法聪明的解法:分支限界法直观理解分支限界法的步骤0-1背包问题问题描述问题建模问题分析1. 定义问题的解空间,确定易于搜索…...
PHP Cookie
Cookie 是什么? cookie 常用于识别用户。cookie 是一种服务器留在用户计算机上的小文件。每当同一台计算机通过浏览器请求页面时,这台计算机将会发送 cookie。通过 PHP,您能够创建并取回 cookie 的值。 如何创建 Cookie? setcoo…...
Java后端面试场景题汇总
1.50 亿数据如何去重&排序? 如此大的数据集进行去重(例如50亿数据条目),我们需要考虑内存和存储空间的限制,同时还需要有一个高效的算法。一般来说,这样的数据量无法直接载入内存进行处理,因此需要采用磁盘存储和分布式处理的技术。主要有以下几种思路: 外部排序…...

【量化中的复权数据详解】
【复权计算方法】 股票会时不时的发生现金分红、送股等一系列股本变动,这会造成股价的非正常变化,导致我们不能直接通过股价来计算股票的涨跌幅。例如一个股票是10元,当他10送10的时候,它的价格会变成5元,但是我们并不…...
YOLO简史
【欢迎关注编码小哥,学习更多实用的编程方法和技巧】 YOLO历史 YOLO (You Only Look Once) 是一种流行的对象检测和图像分割模型,由华盛顿大学的 Joseph Redmon 和 Ali Farhadi 开发。YOLO 于 2015 年推出,因其高速和…...

低通滤波器,高通滤波器,公式
1 低通滤波器 :输出的是电容的电压 1 低通滤波器可以把低频信号上面的高频信号给滤掉 2 100hz正常通过 3 经过低通滤波器后,波形光滑,绿色波形。一致 4 电容充电速度跟不上输入信号的速度(因为加了电阻,限制了电流&…...

深入了解IPv6——光猫相关设定:DNS来源、DHCPv6服务、前缀来源等
光猫IPv6设置后的效果对比图: 修改前: 修改后: 一、DNS来源 1. 网络连接 来源: 从上游网络(如运营商)获取 IPv6 DNS 信息,通过 PPPoE 或 DHCPv6 下发。 特点: DNS 服务器地址直…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究
摘要:在消费市场竞争日益激烈的当下,传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序,探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式,分析沉浸式体验的优势与价值…...

Tauri2学习笔记
教程地址:https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引:https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多,我按照Tauri1的教程来学习&…...

npm安装electron下载太慢,导致报错
npm安装electron下载太慢,导致报错 背景 想学习electron框架做个桌面应用,卡在了安装依赖(无语了)。。。一开始以为node版本或者npm版本太低问题,调整版本后还是报错。偶尔执行install命令后,可以开始下载…...