04.three官方示例+编辑器+AI快速学习webgl_animation_skinning_additive_blending
本实例主要讲解内容
这个示例展示了Three.js中**骨骼动画的叠加混合(Additive Animation Blending)**技术。通过加载一个机器人模型,演示了如何在基础动画(如站立、行走、跑步)之上叠加额外的动画效果(如潜行姿态、悲伤表情、点头同意等),实现更丰富的角色表现。
核心技术包括:
- 如何将动画设置为叠加模式
- 基础动画与叠加动画的权重控制
- 动画之间的平滑过渡
- 叠加动画参考帧的处理
完整代码注释
<!DOCTYPE html>
<html lang="en"><head><title>three.js webgl - additive animation - skinning</title><meta charset="utf-8"><meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"><link type="text/css" rel="stylesheet" href="main.css"><style>a {color: blue;}.control-inactive button {color: #888; // 非活动状态的控制面板按钮样式}</style></head><body><div id="container"></div> <!-- 渲染场景的容器 --><div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - Skeletal Additive Animation Blending(model from <a href="https://www.mixamo.com/" target="_blank" rel="noopener">mixamo.com</a>)<br/></div><!-- 导入映射,指定模块导入路径 --><script type="importmap">{"imports": {"three": "../build/three.module.js","three/addons/": "./jsm/"}}</script><script type="module">// 导入Three.js核心库和辅助工具import * as THREE from 'three';import Stats from 'three/addons/libs/stats.module.js'; // 性能统计工具import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; // GUI控制面板import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; // 轨道控制器import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; // GLTF模型加载器// 全局变量定义let scene, renderer, camera, stats; // 场景、渲染器、相机和性能统计let model, skeleton, mixer, clock; // 模型、骨骼辅助工具、动画混合器和时钟const crossFadeControls = []; // 存储淡入淡出控制按钮的数组let currentBaseAction = 'idle'; // 当前基础动画动作const allActions = []; // 存储所有动画动作的数组// 基础动画动作配置,包含权重信息const baseActions = {idle: { weight: 1 },walk: { weight: 0 },run: { weight: 0 }};// 叠加动画动作配置,包含权重信息const additiveActions = {sneak_pose: { weight: 0 },sad_pose: { weight: 0 },agree: { weight: 0 },headShake: { weight: 0 }};let panelSettings, numAnimations; // 控制面板设置和动画数量// 初始化函数init();function init() {// 获取渲染容器const container = document.getElementById( 'container' );clock = new THREE.Clock(); // 创建时钟用于计算动画时间增量// 创建场景并设置背景和雾scene = new THREE.Scene();scene.background = new THREE.Color( 0xa0a0a0 );scene.fog = new THREE.Fog( 0xa0a0a0, 10, 50 );// 添加半球光,提供自然光照效果const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x8d8d8d, 3 );hemiLight.position.set( 0, 20, 0 );scene.add( hemiLight );// 添加方向光,用于产生阴影const dirLight = new THREE.DirectionalLight( 0xffffff, 3 );dirLight.position.set( 3, 10, 10 );dirLight.castShadow = true;// 设置阴影相机参数,控制阴影范围和精度dirLight.shadow.camera.top = 2;dirLight.shadow.camera.bottom = - 2;dirLight.shadow.camera.left = - 2;dirLight.shadow.camera.right = 2;dirLight.shadow.camera.near = 0.1;dirLight.shadow.camera.far = 40;scene.add( dirLight );// 创建地面平面const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 100, 100 ), new THREE.MeshPhongMaterial( { color: 0xcbcbcb, depthWrite: false } ) );mesh.rotation.x = - Math.PI / 2; // 旋转平面使其水平mesh.receiveShadow = true; // 地面接收阴影scene.add( mesh );// 加载GLTF格式模型const loader = new GLTFLoader();loader.load( 'models/gltf/Xbot.glb', function ( gltf ) {model = gltf.scene; // 获取模型场景对象scene.add( model ); // 将模型添加到场景中// 遍历模型的所有对象,设置可投射阴影model.traverse( function ( object ) {if ( object.isMesh ) object.castShadow = true;} );// 创建骨骼辅助工具,用于可视化骨骼结构skeleton = new THREE.SkeletonHelper( model );skeleton.visible = false; // 默认不显示骨骼scene.add( skeleton );// 获取模型中的所有动画const animations = gltf.animations;mixer = new THREE.AnimationMixer( model ); // 创建动画混合器numAnimations = animations.length; // 记录动画数量// 处理所有动画for ( let i = 0; i !== numAnimations; ++ i ) {let clip = animations[ i ]; // 获取当前动画片段const name = clip.name; // 获取动画名称if ( baseActions[ name ] ) { // 如果是基础动画// 创建动画动作并激活const action = mixer.clipAction( clip );activateAction( action );baseActions[ name ].action = action; // 存储动画动作allActions.push( action ); // 将动作添加到所有动作数组} else if ( additiveActions[ name ] ) { // 如果是叠加动画// 将动画设置为叠加模式THREE.AnimationUtils.makeClipAdditive( clip );// 如果是姿态类动画,提取特定帧作为姿态if ( clip.name.endsWith( '_pose' ) ) {clip = THREE.AnimationUtils.subclip( clip, clip.name, 2, 3, 30 );}// 创建动画动作并激活const action = mixer.clipAction( clip );activateAction( action );additiveActions[ name ].action = action; // 存储动画动作allActions.push( action ); // 将动作添加到所有动作数组}}// 创建控制面板createPanel();// 设置渲染循环renderer.setAnimationLoop( animate );} );// 初始化WebGL渲染器renderer = new THREE.WebGLRenderer( { antialias: true } );renderer.setPixelRatio( window.devicePixelRatio ); // 设置像素比,适配高DPI屏幕renderer.setSize( window.innerWidth, window.innerHeight ); // 设置渲染尺寸renderer.shadowMap.enabled = true; // 启用阴影渲染container.appendChild( renderer.domElement ); // 将渲染器DOM元素添加到容器中// 设置相机camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 100 );camera.position.set( - 1, 2, 3 );// 添加轨道控制器,允许用户旋转和缩放场景const controls = new OrbitControls( camera, renderer.domElement );controls.enablePan = false; // 禁用平移controls.enableZoom = false; // 禁用缩放controls.target.set( 0, 1, 0 ); // 设置控制器目标点controls.update(); // 更新控制器状态// 添加性能统计面板stats = new Stats();container.appendChild( stats.dom );// 添加窗口大小变化事件监听,调整相机和渲染器window.addEventListener( 'resize', onWindowResize );}// 创建控制面板函数function createPanel() {// 创建GUI面板const panel = new GUI( { width: 310 } );// 创建不同功能的折叠面板const folder1 = panel.addFolder( 'Base Actions' ); // 基础动画控制面板const folder2 = panel.addFolder( 'Additive Action Weights' ); // 叠加动画权重控制面板const folder3 = panel.addFolder( 'General Speed' ); // 全局速度控制面板// 初始化控制面板设置panelSettings = {'modify time scale': 1.0 // 动画全局速度设置};// 获取所有基础动画名称,并添加"None"选项const baseNames = [ 'None', ...Object.keys( baseActions ) ];// 为每个基础动画添加控制按钮for ( let i = 0, l = baseNames.length; i !== l; ++ i ) {const name = baseNames[ i ];const settings = baseActions[ name ];// 为每个基础动画按钮设置点击事件panelSettings[ name ] = function () {// 获取当前和目标动画动作const currentSettings = baseActions[ currentBaseAction ];const currentAction = currentSettings ? currentSettings.action : null;const action = settings ? settings.action : null;// 如果当前动画和目标动画不同,则执行过渡if ( currentAction !== action ) {prepareCrossFade( currentAction, action, 0.35 );}};// 将控制按钮添加到面板并存储到控制数组crossFadeControls.push( folder1.add( panelSettings, name ) );}// 为每个叠加动画添加权重控制滑块for ( const name of Object.keys( additiveActions ) ) {const settings = additiveActions[ name ];// 添加滑块并监听变化panelSettings[ name ] = settings.weight;folder2.add( panelSettings, name, 0.0, 1.0, 0.01 ).listen().onChange( function ( weight ) {setWeight( settings.action, weight ); // 设置动画权重settings.weight = weight; // 更新设置中的权重值} );}// 添加全局速度控制滑块folder3.add( panelSettings, 'modify time scale', 0.0, 1.5, 0.01 ).onChange( modifyTimeScale );// 默认打开所有折叠面板folder1.open();folder2.open();folder3.open();// 为控制按钮添加激活/禁用状态样式crossFadeControls.forEach( function ( control ) {// 设置按钮为非活动状态control.setInactive = function () {control.domElement.classList.add( 'control-inactive' );};// 设置按钮为活动状态control.setActive = function () {control.domElement.classList.remove( 'control-inactive' );};// 根据初始权重设置按钮状态const settings = baseActions[ control.property ];if ( ! settings || ! settings.weight ) {control.setInactive();}} );}// 激活动画动作的函数function activateAction( action ) {// 获取动画片段和对应的设置const clip = action.getClip();const settings = baseActions[ clip.name ] || additiveActions[ clip.name ];// 设置动画权重并播放setWeight( action, settings.weight );action.play();}// 修改动画全局速度的函数function modifyTimeScale( speed ) {mixer.timeScale = speed; // 设置动画混合器的时间缩放}// 准备动画过渡的函数function prepareCrossFade( startAction, endAction, duration ) {// 如果当前动画是"idle",或者没有起始或目标动画,立即执行过渡if ( currentBaseAction === 'idle' || ! startAction || ! endAction ) {executeCrossFade( startAction, endAction, duration );} else {// 否则等待当前动画完成当前循环后再执行过渡synchronizeCrossFade( startAction, endAction, duration );}// 更新控制按钮状态if ( endAction ) {// 如果有目标动画,更新当前基础动画名称const clip = endAction.getClip();currentBaseAction = clip.name;} else {// 否则设置为"None"currentBaseAction = 'None';}// 更新所有控制按钮的激活/禁用状态crossFadeControls.forEach( function ( control ) {const name = control.property;if ( name === currentBaseAction ) {control.setActive(); // 设置为活动状态} else {control.setInactive(); // 设置为非活动状态}} );}// 同步动画过渡的函数,确保在动画循环结束时进行过渡function synchronizeCrossFade( startAction, endAction, duration ) {// 添加循环结束事件监听mixer.addEventListener( 'loop', onLoopFinished );function onLoopFinished( event ) {if ( event.action === startAction ) { // 当起始动画完成一个循环mixer.removeEventListener( 'loop', onLoopFinished ); // 移除事件监听executeCrossFade( startAction, endAction, duration ); // 执行过渡}}}// 执行动画过渡的函数function executeCrossFade( startAction, endAction, duration ) {if ( endAction ) { // 如果有目标动画// 设置目标动画权重为1,并重置时间setWeight( endAction, 1 );endAction.time = 0;if ( startAction ) {// 从起始动画过渡到目标动画(带warping效果)startAction.crossFadeTo( endAction, duration, true );} else {// 淡入目标动画(没有起始动画)endAction.fadeIn( duration );}} else { // 如果没有目标动画// 淡出起始动画startAction.fadeOut( duration );}}// 设置动画权重的函数function setWeight( action, weight ) {action.enabled = true; // 启用动画action.setEffectiveTimeScale( 1 ); // 设置时间缩放为1action.setEffectiveWeight( weight ); // 设置动画权重}// 窗口大小变化事件处理函数function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight; // 更新相机宽高比camera.updateProjectionMatrix(); // 更新相机投影矩阵renderer.setSize( window.innerWidth, window.innerHeight ); // 更新渲染器尺寸}// 动画循环函数,每一帧都会被调用function animate() {// 更新所有动画的权重信息for ( let i = 0; i !== numAnimations; ++ i ) {const action = allActions[ i ];const clip = action.getClip();// 更新基础动画或叠加动画的权重const settings = baseActions[ clip.name ] || additiveActions[ clip.name ];settings.weight = action.getEffectiveWeight();}// 获取自上一帧以来的时间增量,用于更新动画混合器const mixerUpdateDelta = clock.getDelta();// 更新动画混合器,渲染场景,并更新性能统计mixer.update( mixerUpdateDelta );renderer.render( scene, camera );stats.update();}</script></body>
</html>
整体总结
这个Three.js示例展示了骨骼动画的叠加混合技术,主要内容包括:
-
叠加动画原理:
- 叠加动画基于参考帧计算偏移量,可在基础动画之上叠加额外效果
- 使用
THREE.AnimationUtils.makeClipAdditive()
将动画设置为叠加模式 - 对于姿态类动画,使用
subclip()
提取特定帧作为参考姿态
-
动画控制系统:
- 基础动画系统:站立、行走、跑步等主要动作
- 叠加动画系统:潜行姿态、悲伤表情、点头等次要动作
- 动画过渡:使用
crossFadeTo()
和fadeIn/fadeOut()
实现平滑过渡
-
用户交互:
- 基础动画切换:通过按钮在不同基础动画间切换
- 叠加动画权重控制:通过滑块调整各叠加动画的影响程度
- 全局速度控制:调整所有动画的播放速度
这种叠加动画技术在游戏、虚拟角色动画等场景中非常有用,可以让角色表现更加丰富多样,同时保持动画系统的灵活性和可维护性。
相关文章:
04.three官方示例+编辑器+AI快速学习webgl_animation_skinning_additive_blending
本实例主要讲解内容 这个示例展示了Three.js中**骨骼动画的叠加混合(Additive Animation Blending)**技术。通过加载一个机器人模型,演示了如何在基础动画(如站立、行走、跑步)之上叠加额外的动画效果(如潜行姿态、悲伤表情、点头同意等),实现更丰富的角…...
WSL配置docker启动nacos容器load derby-schema.sql error.问题解决方案
nacos配置问题 问题再现查看错误logsQWEN的解答不挂载/data结论 问题再现 本来想要本地跑一下nacos,之前都是直接在Linux环境下面,现在就使用windows的wsl跑一下nacos,之前是需要先配置/conf文件下面的porperties文件以及构建对应的nacos-config数据库。所以我使用…...
游戏引擎学习第272天:显式移动转换
回顾并为今天的内容铺垫背景 我们刚开始为游戏主角编写一些程序逻辑,因为我们之前已经完成了大部分引擎方面的开发,现在可以专注在角色身上。这个角色的移动方式会有些特别,与大多数游戏角色的运动机制不太一样。我们当前正在实现的控制方式…...
AVL树解析
插入操作 // 插入操作 bool insert(const pair<K, V>& kv) {// 若树为空,直接构造,new一个if (_root nullptr) {_root new Node(kv);return true;}// 用于遍历树的当前节点Node* cur _root;// 用于记录当前节点的父节点Node* parent n…...
vue 中的数据代理
在 Vue 中,数据代理(Data Proxy) 是 Vue 实现 MVVM 模式 的关键技术之一。Vue 使用数据代理让你可以通过 this.message 访问 data.message,而不需要写 this.data.message —— 这大大简化了模板和逻辑代码。 我们来深入理解它的本…...
Linux共享内存深度解析:从内核机制到云原生应用
引言:超越进程边界的内存魔术 在Linux系统的进程间通信(IPC)领域,共享内存(Shared Memory)如同魔法镜子般的存在——不同进程透过它看到相同的内存镜像。这种机制摒弃了数据拷贝,直击性能瓶颈&…...
Vue Router全局拦截
Vue Router全局拦截全攻略 一、为什么需要全局拦截? 最近在开发后台管理系统时,突然发现所有页面都需要登录才能访问。如果每个页面都手动检查登录状态,那代码简直要写成意大利面条了。这时候,Vue Router的全局拦截功能就像个贴…...
从0开始学linux韦东山教程第三章问题小结(3)
本人从0开始学习linux,使用的是韦东山的教程,在跟着课程学习的情况下的所遇到的问题的总结,理论虽枯燥但是是基础。说实在的越看视频越感觉他讲的有点乱后续将以他的新版PDF手册为中心,视频作为辅助理解的工具。参考手册为嵌入式Linux应用开发…...
【前端】【css】【总复习】三万字详解CSS 知识体系
🌈 CSS 知识体系目录大纲 一、基础知识入门 1. CSS 简介与作用 CSS(Cascading Style Sheets,层叠样式表)是一种用于给 HTML 页面添加样式的语言,作用是让网页更美观、结构更清晰、布局更灵活。 核心作用: 控制网页元素的 颜色、字体、间距、边框、背景布局网页元素位置…...
Linux 进程等待
1、进程等待 僵尸进程 是一个比较麻烦的问题,如果不对其做出处理,僵尸进程 就会越来越多,导致 内存泄漏 和 标识符 占用问题 进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为…...
轻量服务器与宝塔
因为访问宝塔面板是需要在安全组设置一下开放端口,比如这里是42450 但是我们用的轻量服务器是把安全组这种功能削减了的,所以我就去尝试修改了一下防火墙设置 然后就可以访问了...
深入理解AMBA总线(六)AHB-lite Slave响应和其它控制信号
上一篇文章给大家介绍了AHB-lite的一些控制信号,重点是通过这些控制信号去理解AHB-lite为什么这么设计,采用这些控制信号有什么好处。这节课给大家带来剩余的一些控制信号介绍。 ** 1、Slave Response Signaling ** 1.1、Slave Transfer Responses …...
app加固
1、什么是加固? 我们之前讲的逆向,大多数都是用加密算法去加密一些明文字符串,然后把得到的结果用 Base64、Hex等进行编码后提交。加固其实也一样,只不过他通常加密的是 dex文件而已。但是 dex 文件加密以后,安卓系统是没法直接运行的。所以加固的核心&…...
Linux架构篇、第三章_2_Linux服务器监控与NGINX优化
Linux_架构篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目: 版本号: 1.0,0 作者: 老王要学习 日期: 2025.05.11 适用环境: Centos7 文档说明 本文围绕 Linux 服务器监控与 NGINX 优化展开。介绍了 sysst…...
第26节:卷积神经网络(CNN)-数据增强技术(PyTorch)
1. 引言 在深度学习领域,数据增强(Data Augmentation)是提升卷积神经网络(CNN)性能的关键技术之一。通过人为地扩展训练数据集,数据增强能够有效提高模型的泛化能力,防止过拟合,特别是在训练数据有限的情况下。本文将全面介绍PyTorch框架下的数据增强技术,包括基本原理、…...
架构思维:通用架构模式_怀疑下游的设计思路与最佳实践
文章目录 1. 引言2. 为什么要“怀疑下游”3. 三大类下游依赖及应对方案3.1 对其他微服务的依赖3.1.1 分布式事务简易补偿方案3.2 对数据库的依赖3.3 对消息中间件的依赖 4. 分布式事务实战案例5. 小结 1. 引言 在 架构思维:通用架构模式_从设计到代码构建稳如磐石的…...
[Java实战]Spring Boot 中Starter机制与自定义Starter实战(九)
[Java实战]Spring Boot 中Starter机制与自定义Starter实战(九) 引言 Spring Boot 的 Starter 是其“约定优于配置”理念的核心体现,通过简化依赖管理和自动配置,极大提升了开发效率。本文将深入剖析 Starter 的设计思想、实现原…...
C++23 views::repeat (P2474R2) 深入解析
文章目录 引言C20 Ranges库回顾什么是Rangesstd::views的作用 views::repeat概述基本概念原型定义工作原理应用场景初始化容器模拟测试数据 总结 引言 在C的发展历程中,每一个新版本都会带来一系列令人期待的新特性,这些特性不仅提升了语言的性能和表达…...
【第三十五周】Janus-pro 技术报告阅读笔记
Janus-Pro 摘要Abstract文章信息引言方法Janus 架构Janus 训练Janus-Pro 的改进 实验结果总结 摘要 本篇博客介绍了Janus-Pro,这是一个突破性的多模态理解与生成统一模型,其核心思想是通过解耦双路径视觉编码架构解决传统方法中语义理解与像素生成的任务…...
基于Qt的app开发第七天
写在前面 笔者是大一下计科生,标题这个项目是笔者这个学期的课设,与学长共创,我负责客户端部分,现在已经实现了待办板块的新建、修改。 这个项目目前已经走上正轨了,博主也实现了主要功能的从无到有ÿ…...
第二十二节:图像金字塔-拉普拉斯金字塔
在数字图像处理的奇幻世界中,存在着一种能够连接不同视觉维度的神秘阶梯——图像金字塔。这种独特的结构让计算机视觉算法能够在不同尺度下观察和理解图像特征,而其中的拉普拉斯金字塔更是隐藏着图像细节重构的终极奥秘。 一、金字塔的数学基础:从高斯到拉普拉斯 1.1 高斯金…...
Flutter基础()
导航栏 appBar: AppBar() title: const Text(搜索) //标题 backgroundColor: Colors.blue //背景颜色 centerTitle: true //标题居中leading 属性 作用: 放置在应用栏左侧的控件,通常是一个图标按钮,用于导航或打开菜单。 AppBar(le…...
ES面试题系列「一」
1、Elasticsearch 是什么?它与传统数据库有什么区别? 答案:Elasticsearch 是一个基于 Lucene 的分布式、开源的搜索和分析引擎,主要用于处理大量的文本数据,提供快速的搜索和分析功能。与传统数据库相比,E…...
Oracle 通过 ROWID 批量更新表
Oracle 通过 ROWID 批量更新表 在 Oracle 数据库中,使用 ROWID 进行批量更新是一种高效的更新方法,因为它直接定位到物理行位置,避免了通过索引查找的开销。 ROWID 基本概念 ROWID 是 Oracle 数据库中每一行的唯一物理地址标识符ÿ…...
罗技无线鼠标的配对方法
罗技鼠标的配对方法: 重新连接鼠标 请按照以下步骤将鼠标与 USB 接收器重新配对。 1.将USB接收器插入计算机。 2.将鼠标关闭电源。 3.按住并持续按住向右按钮,直到操作结束。 4.切换鼠标电源。 5. 单击一次左侧按钮。 6. 单击一次中间按钮。 7.全部松开&…...
移动应用开发的六大设计原则
在移动应用开发中,遵循设计原则能大幅提升代码的可维护性和扩展性。本文以一个简单的学生管理系统为例,解析六大核心设计原则的实践方法。 1. 单一职责原则 优点: 提高可维护性:一个类只负责一项职责,代码的功能会更…...
LLM初识
从零到一:用 Python 和 LLM 构建你的专属本地知识库问答机器人 摘要: 随着大型语言模型(LLM)的兴起,构建智能问答系统变得前所未有的简单。本文将详细介绍如何使用 Python,结合开源的 LLM 和向量数据库技…...
【CTF】Linux Shell RCE绕过(bypass)技术总结
在Linux环境下,远程代码执行(RCE,Remote Code Execution)是一种常见的攻击手段。然而,许多系统会对命令注入进行过滤或限制,例如禁止特定关键字(如system)、斜杠(/&#…...
深入理解 Linux 权限控制机制
引言 在 Linux 系统中,权限控制是保障系统安全的核心机制。通过限制用户对文件和资源的访问,它能有效防止未授权操作,保护数据不被篡改或泄露。合理设置权限不仅有助于实现用户隔离和最小权限原则,还能降低系统被滥用或攻击的风险…...
技术书籍推荐(002)
20. 利用Python进行数据分析 免费 电子书 PDF 下载 书籍简介: 本书聚焦于使用Python进行数据处理和分析。详细介绍了Python中用于数据分析的重要库,如NumPy(提供高效的数值计算功能,包括数组操作、数学函数等)、panda…...
[SAP] SAP ERP用户参数设置
菜单路径 个人默认值的设置(数值,日期,时间) 个人参数值的设置 在给参数设置参数值后,当用户登录到对应功能页面时,这些参数值就会自动带出来 例如,我们分别对上述AAT,VKO以及VTW这3个参数设置了参数值&am…...
Python中,async和with结合使用,有什么好处?
在Python的异步编程中,async和with的结合使用(即async with)为开发者提供了一种优雅且高效的资源管理模式。这种组合不仅简化了异步代码的编写,还显著提升了程序的健壮性和可维护性。以下是其核心优势及典型应用场景的分析&#x…...
【信息系统项目管理师】第3章:信息系统治理 - 29个经典题目及详解
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 【第1题】【第2题】【第3题】【第4题】【第5题】【第6题】【第7题】【第8题】【第9题】【第10题】【第11题】【第12题】【第13题】【第14题】【第15题】【第16题】【第17题】【第18题】【第19题】【第20题】【第…...
python实战项目69:基于Python爬虫的链家二手房数据采集方法研究
python实战项目69:链家二手房数据采集 一、项目需求1.1 房地产数据价值1.2 传统数据获取局限性1.3 技术可行性二、数据采集流程2.1 需求分析2.2 网页结构分析2.3 请求发送与反爬策略2.4 数据解析2.5 数据存储三、结论与展望四、完整代码一、项目需求 本文针对房地产数据分析需…...
PowerShell 脚本中文乱码处理
问题描述 脚本带中文,执行时命令行窗口会显示出乱码 示例 Write-Host "测试成功!"解决方法 问了DeepSeek,让确认是不是 UTF8 无 BOM 格式 事实证明方向对了 但是确认信息有偏差 改成 UTF8 with BOM 使用任意支持修改编码的文本…...
SpEL(Spring Expression Language)使用详解
SpEL(Spring Expression Language)是 Spring 框架中一种强大的表达式语言,支持在运行时动态查询和操作对象图。它与 Spring 生态深度集成,广泛应用于依赖注入、数据绑定、AOP、安全规则等场景。以下是其核心语法、应用场景及使用示…...
论文阅读与写作:《从探索到突破:解密科研和论文写作的思维密码》
文章目录 一、如何做科研1.科研的步骤2.课题选择3.快速入门一个新领域:读论文,先读综述(1)自己看论文的时候,每篇论文花3-5分钟记录一下自己的idea和一些瞬间的想法(2)高质量文献:顶会顶刊(3)如何检索 4.注重团队协作与学术交流5.…...
免费公共DNS服务器推荐
当自动获取的DNS或本地运营商的DNS出现问题,可能导致软件无法连接服务器。此时,手动修改电脑的DNS设置或许能解决问题。许多用户觉得电脑上网速度慢、游戏卡顿,归咎于DNS问题。确实,我们可以自行设置一个DNS来改善网络体验。不少用…...
CK-S650-PA60S半导体专用读写器|读写头与绿联RS232串口转接头联机测试说明
CK-S650-PA60S半导体专用读写器|读写头是一款国产替代欧姆龙V640系列,支持德州仪器公司(TI)制造的RI-TRP-DR2B、RI-TRP-WR2B-40低频玻璃管标签|射频标签读写,广泛应用半导体硅片盒、晶圆盒、花篮等RFID插件识别与产品追溯领域。 CK-S650-PA60S半导体专用…...
Babel 基础使用指南:从安装到编译的完整流程
Babel 是当今前端开发中不可或缺的工具,它允许开发者使用最新的 JavaScript 特性,同时确保代码能在各种浏览器和环境中运行。本文将带你从零开始学习 Babel 的基础使用,涵盖安装、配置到实际编译的全过程。 1. 安装 Babel 和插件 首先&…...
uniapp-商城-52-后台 商家信息(商家信息数据,云对象使用)
1、概述 已经通过好几个篇幅来说明商家信息,包括logo、商家名称,地址,电话以及商家简介。通过表单组件和标签,以及我们的文件上传标签,都做了说明。(logo上传,用的文件上传组件是上传到公共的数…...
【我的创作纪念日】512
机缘 还记得 2023 年 12 月 15 日,我撰写了第 1 篇技术博客:《oracle 跟踪文件--审计日志》。从此,这平凡的一天,赋予了我不平凡的意义。在那一刻,我已在创作这趟旅程中出发,并立志将工作中的经验总结&…...
共享内存与信号量结合
在Linux系统中,进程间通信(IPC)和原子性是并发编程中的核心问题。以下是对这些概念的详细分步解释: 一、进程间通信(IPC)方法 1. 管道(Pipe) 匿名管道:用于父子进程等有…...
西门子PLC s7-1200工艺对象“轴”的应用(1)
1. 工艺对象 “ 轴 ” 的概念 在西门子 S7-1200 PLC 中,术语 “ 轴 ” 特指用 “ 轴 ” 工艺对象表示的驱动器工艺映像。 “ 轴 ” 工艺对象是用 户程序与驱动器之间的接口,用于接收用户程序中的运动控制命令、执行这些命令并监视其运行情况。 运动控制…...
ubuntu22.04在 Docker容器中安装 ROS2-Humble
22.04 安装 docker 容器并实现rviz功能 1 docker pull命令拉取包含ROS-Humble的镜像: docker pull osrf/ros:humble-desktop-full-jammy docker images验证该镜像是否拉取成功。 使用镜像osrf/ros:humble-desktop-full-jammy创建并运行容器 sudo docker run -it…...
【LwIP源码学习6】UDP部分源码分析
前言 本文对lwip协议栈里UDP部分的源码进行分析。将源码中最关键部分提取出来,梳理UDP部分的设计框架。 应用层例子 首先举一个应用层使用UDP的简单例子,如下: //第一步:申请套接字 sock socket(AF_INET, SOCK_DGRAM, 0); //…...
Python Cookbook-7.10 在 MySQL 数据库中储存 BLOB
任务 想把一个二进制的大对象(BLOB)存入MySQL数据库 解决方案 MySQLdb 模块并不支持完整的占位符,不过可以使用模块的escape_string 函数来解决: import MySQLdb,cPickle #连接到数据库,用你的本机来测试数据库,并获得游标 connection = MySQLdb.connect(db = "tes…...
LWIP的ICMP协议
ICMP协议简介 ICMP协议是一个网络层协议 背景:如果丢包了,IP协议并不能通知传输层是否丢包以及丢包的原因。因此我们需要ICMP协议来完成这样的功能 为什么需要ICMP协议 1,IP 协议本身不提供差错报告和差错控制机制来保证数据报递交的有效…...
Spring Web MVC响应
返回静态页面 第一步 创建html时,要注意创建的路径,要在static下面 第二步 把需要写的内容写到body内 第三步 直接访问路径就可以 返回数据ResponseBody RestController Controller ResponseBody Controller:返回视图 ResponseBody&…...
STM32智能窗帘系统:从零到一的开发实战
简介 智能窗帘控制系统是智能家居领域的经典应用,随着物联网技术的不断发展,其功能从简单的远程控制演变为具备环境感知、多设备联动和跨平台兼容的智能系统。本篇文章将全面解析基于STM32的智能窗帘系统开发方案,从硬件设计到软件实现,再到企业级扩展功能,提供完整的代码…...