Open GL ES ->GLSurfaceView在正交投影下的图片旋转、缩放、位移

XML文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><com.example.myapplication.MyMatrixGLSurfaceViewandroid:id="@+id/glSurfaceView"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="4" /><!-- 旋转控制按钮 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="旋转:"android:textSize="16sp" /><Buttonandroid:id="@+id/rotateXBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="X轴" /><Buttonandroid:id="@+id/rotateYBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Y轴" /><Buttonandroid:id="@+id/rotateZBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Z轴" /></LinearLayout><!-- 缩放控制按钮 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="缩放:"android:textSize="16sp" /><Buttonandroid:id="@+id/scaleXBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="X轴" /><Buttonandroid:id="@+id/scaleYBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Y轴" /></LinearLayout><!-- 位移控制按钮 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="位移:"android:textSize="16sp" /><Buttonandroid:id="@+id/translateXPosBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="X轴" /><Buttonandroid:id="@+id/translateYPosBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Y轴" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="正交投影:" /><Buttonandroid:id="@+id/resetBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="重置" /></LinearLayout></LinearLayout>

Activity代码

class MainActivity5 : AppCompatActivity(), View.OnClickListener {companion object {// 变换增量private const val ROTATION_DELTA = 10.0fprivate const val SCALE_DELTA = 0.25fprivate const val TRANSLATE_DELTA = 0.25f}private lateinit var glSurfaceView: MyMatrixGLSurfaceView// 变换参数private var rotationX = 0fprivate var rotationY = 0fprivate var rotationZ = 0fprivate var scaleX = 1.0fprivate var scaleY = 1.0fprivate var translateX = 0fprivate var translateY = 0foverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main5)// 初始化GLSurfaceViewglSurfaceView = findViewById(R.id.glSurfaceView)// 初始化所有按钮并设置点击监听器findViewById<Button>(R.id.rotateXBtn).setOnClickListener(this)findViewById<Button>(R.id.rotateYBtn).setOnClickListener(this)findViewById<Button>(R.id.rotateZBtn).setOnClickListener(this)findViewById<Button>(R.id.scaleXBtn).setOnClickListener(this)findViewById<Button>(R.id.scaleYBtn).setOnClickListener(this)findViewById<Button>(R.id.translateXPosBtn).setOnClickListener(this)findViewById<Button>(R.id.translateYPosBtn).setOnClickListener(this)findViewById<Button>(R.id.resetBtn).setOnClickListener(this)}override fun onClick(v: View) {when (v.id) {// 处理旋转R.id.rotateXBtn -> {rotationX += ROTATION_DELTAglSurfaceView.updateRotation(rotationX, AXIS.X)}R.id.rotateYBtn -> {rotationY += ROTATION_DELTAglSurfaceView.updateRotation(rotationY, AXIS.Y)}R.id.rotateZBtn -> {rotationZ += ROTATION_DELTAglSurfaceView.updateRotation(rotationZ, AXIS.Z)}// 处理缩放R.id.scaleXBtn -> {scaleX += SCALE_DELTAglSurfaceView.updateScale(scaleX, AXIS.X)}R.id.scaleYBtn -> {scaleY += SCALE_DELTAglSurfaceView.updateScale(scaleY, AXIS.Y)}// 处理位移R.id.translateXPosBtn -> {translateX += TRANSLATE_DELTAglSurfaceView.updateTranslate(translateX, AXIS.X)}R.id.translateYPosBtn -> {translateY += TRANSLATE_DELTAglSurfaceView.updateTranslate(translateY, AXIS.Y)}// 重置所有变换R.id.resetBtn -> {resetTransformations()}}// 更新渲染glSurfaceView.requestRender()}private fun resetTransformations() {// 重置所有变换参数rotationX = 0frotationY = 0frotationZ = 0fscaleX = 1.0fscaleY = 1.0ftranslateX = 0ftranslateY = 0fglSurfaceView?.reset()}
}enum class AXIS {X,Y,Z
}

GLSurfaceView代码

class MyMatrixGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {private var mRenderer = MyMatrixRenderer(context)init {// 设置 OpenGL ES 3.0 版本setEGLContextClientVersion(3)setRenderer(mRenderer)// 设置渲染模式, 仅在需要重新绘制时才进行渲染,以节省资源renderMode = RENDERMODE_WHEN_DIRTY}fun updateRotation(angle: Float, axis: AXIS) {mRenderer?.mDrawData?.apply {resetMatrix()computeRotateModelMatrix(angle, axis)computeMVPMatrix(width, height)drawCurrentOutput()}}fun updateScale(scale: Float, axis: AXIS) {mRenderer?.mDrawData?.apply {resetMatrix()computeScaleModelMatrix(scale, axis)computeMVPMatrix(width, height)drawCurrentOutput()}}fun updateTranslate(translate: Float, axis: AXIS) {mRenderer?.mDrawData?.apply {resetMatrix()computeTranslateModelMatrix(translate, axis)computeMVPMatrix(width, height)drawCurrentOutput()}}fun reset() {mRenderer?.mDrawData?.apply {resetMatrix()computeMVPMatrix(width, height)drawCurrentOutput()}}
}

GLSurfaceView.Renderer代码

class MyMatrixRenderer(private val mContext: Context) : GLSurfaceView.Renderer {var mDrawData: MatrixDrawData? = nulloverride fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {// 当 Surface 创建时调用, 进行 OpenGL ES 环境的初始化操作, 设置清屏颜色为青蓝色 (Red=0, Green=0.5, Blue=0.5, Alpha=1)GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)mDrawData = MatrixDrawData().apply {initTexture0(mContext, R.drawable.picture)initShader()initVertexBuffer()}}override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {// 当 Surface 尺寸发生变化时调用,例如设备的屏幕方向发生改变, 设置视口为新的尺寸,视口是指渲染区域的大小GLES30.glViewport(0, 0, width, height)mDrawData?.resetMatrix()mDrawData?.computeMVPMatrix(width, height)}override fun onDrawFrame(gl: GL10?) {GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)mDrawData?.drawCurrentOutput()}
}

GLSurfaceView.Renderer需要的绘制数据

class MatrixDrawData {private var NO_OFFSET = 0private val VERTEX_POS_DATA_SIZE = 3private val TEXTURE_POS_DATA_SIZE = 2private var mProgram: Int = -1// VAO(Vertex Array Object), 顶点数组对象, 用于存储VBOprivate var mVAO = IntArray(1)// VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据private var mVBO = IntArray(2)// IBO(Index Buffer Object), 索引缓冲对象,用于存储顶点索引数据private var mIBO = IntArray(1)// 纹理IDvar mTextureID = IntArray(1)// 有上下翻转的变换矩阵private var mMVPMatrix = FloatArray(16)// 投影矩阵private val mProjectionMatrix = FloatArray(16)// 观察矩阵private val mViewMatrix = FloatArray(16)// 模型矩阵private val mModelMatrix = FloatArray(16)// 视口比例private var mViewPortRatio = 1f// 准备顶点坐标,分配直接内存// OpenGL ES坐标系:原点在中心,X轴向右为正,Y轴向上为正,Z轴向外为正val vertex = floatArrayOf(-1.0f, 1.0f, 0.0f, // 左上-1.0f, -1.0f, 0.0f, // 左下1.0f, 1.0f, 0.0f, // 右上1.0f, -1.0f, 0.0f, // 右下)val vertexBuffer = ByteBuffer.allocateDirect(vertex.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertex).position(NO_OFFSET)// 准备纹理坐标,分配直接内存// 纹理坐标系:原点在左下角,X轴向右为正,Y轴向上为正val textureCoords = floatArrayOf(0.0f, 1.0f, // 左上0.0f, 0.0f, // 左下1.0f, 1.0f, // 右上1.0f, 0.0f, // 右下)val textureBuffer = ByteBuffer.allocateDirect(textureCoords.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(textureCoords).position(NO_OFFSET)// 索引坐标,分配直接内存val index = shortArrayOf(0, 1, 2, // 第一个三角形1, 3, 2, // 第二个三角形)val indexBuffer = ByteBuffer.allocateDirect(index.size * 2).order(ByteOrder.nativeOrder()).asShortBuffer().put(index).position(NO_OFFSET)// 初始化着色器程序fun initShader() {val vertexShaderCode = """#version 300 esuniform mat4 uMVPMatrix; // 变换矩阵in vec4 aPosition; // 顶点坐标in vec2 aTexCoord; // 纹理坐标 out vec2 vTexCoord; void main() {// 输出顶点坐标和纹理坐标到片段着色器gl_Position = uMVPMatrix * aPosition;vTexCoord = aTexCoord;}""".trimIndent()val fragmentShaderCode = """#version 300 esprecision mediump float;uniform sampler2D uTexture_0;in vec2 vTexCoord;out vec4 fragColor;void main() {fragColor = texture(uTexture_0, vTexCoord);}""".trimIndent()// 加载顶点着色器和片段着色器, 并创建着色器程序val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)val fragmentShader =LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)mProgram = GLES30.glCreateProgram()GLES30.glAttachShader(mProgram, vertexShader)GLES30.glAttachShader(mProgram, fragmentShader)GLES30.glLinkProgram(mProgram)// 删除着色器对象GLES30.glDeleteShader(vertexShader)GLES30.glDeleteShader(fragmentShader)}// 创建VAO, VBO, IBOfun initVertexBuffer() {// 绑定VAOGLES30.glGenVertexArrays(mVAO.size, mVAO, NO_OFFSET)GLES30.glBindVertexArray(mVAO[0])// 绑定VBOGLES30.glGenBuffers(mVBO.size, mVBO, NO_OFFSET)// 绑定顶点缓冲区数据到VBO[0]GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[0])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertex.size * 4,vertexBuffer,GLES30.GL_STATIC_DRAW)// 解析顶点缓冲区数据到VBO[0]val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")GLES30.glEnableVertexAttribArray(positionHandle)GLES30.glVertexAttribPointer(positionHandle,VERTEX_POS_DATA_SIZE,GLES30.GL_FLOAT,false,0,NO_OFFSET)// 解绑顶点缓冲区GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)// 绑定纹理缓冲区数据到VBO[1]GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[1])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,textureCoords.size * 4,textureBuffer,GLES30.GL_STATIC_DRAW)// 解析纹理缓冲区数据到VBO[1]val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")GLES30.glEnableVertexAttribArray(textureHandle)GLES30.glVertexAttribPointer(textureHandle,TEXTURE_POS_DATA_SIZE,GLES30.GL_FLOAT,false,0,NO_OFFSET)// 解绑纹理缓冲区GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)// 绑定IBOGLES30.glGenBuffers(mIBO.size, mIBO, NO_OFFSET)// 绑定索引缓冲区数据到IBO[0]GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, mIBO[0])GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER,index.size * 2,indexBuffer,GLES30.GL_STATIC_DRAW)// 解绑VAOGLES30.glBindVertexArray(0)// 解绑IBOGLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0)}// 使用着色器程序绘制图形fun drawSomething(program: Int, mvpMatrix: FloatArray) {// 解析变换矩阵val matrixHandle = GLES30.glGetUniformLocation(program, "uMVPMatrix")GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mvpMatrix, NO_OFFSET)// 绑定VAOGLES30.glBindVertexArray(mVAO[0])// 绘制图形GLES30.glDrawElements(GLES30.GL_TRIANGLES,index.size,GLES30.GL_UNSIGNED_SHORT,NO_OFFSET)// 解绑VAOGLES30.glBindVertexArray(0)}fun resetMatrix() {Matrix.setIdentityM(mModelMatrix, NO_OFFSET)Matrix.setIdentityM(mViewMatrix, NO_OFFSET)Matrix.setIdentityM(mProjectionMatrix, NO_OFFSET)Matrix.setIdentityM(mMVPMatrix, NO_OFFSET)}// 计算GLSurfaceView变换矩阵fun computeMVPMatrix(width: Int, height: Int) {// 视图矩阵Matrix.setLookAtM(mViewMatrix,NO_OFFSET,0f, 0f, 1f,0f, 0f, 0f,0f, 1f, 0f)// 投影矩阵takeIf { width > height }?.let {mViewPortRatio = (width * 1f) / height// 计算包围球半径, 图片360度旋转后的半径val radius = sqrt(1f + mViewPortRatio * mViewPortRatio)// 计算近远平面val near = 0.1fval far = near +  radius * 2fMatrix.orthoM(mProjectionMatrix,NO_OFFSET,-mViewPortRatio,  // 左边界mViewPortRatio,   // 右边界-1f,              // 下边界1f,               // 上边界near,             // 近平面距离far               // 远平面距离)} ?: run {mViewPortRatio = (height * 1f) / width// 计算包围球半径, 图片360度旋转后的半径val radius = sqrt(1f + mViewPortRatio * mViewPortRatio)// 计算近远平面val near = 0.1fval far = near +  radius * 2fMatrix.orthoM(mProjectionMatrix,NO_OFFSET,-1f,              // 左边界1f,               // 右边界-mViewPortRatio,  // 下边界mViewPortRatio,   // 上边界near,             // 近平面距离far               // 远平面距离)}// 最终变换矩阵,第一次变换,模型矩阵 x 视图矩阵 = Model x View, 但是OpenGL ES矩阵乘法是右乘,所以是View x ModelMatrix.multiplyMM(mMVPMatrix,NO_OFFSET,mViewMatrix,NO_OFFSET,mModelMatrix,NO_OFFSET)// 最终变换矩阵,第二次变换,投影矩阵 x 模型矩阵 x 视图矩阵 = Projection x View x Model, 但是OpenGL ES矩阵乘法是右乘,所以是View x Model x ProjectionMatrix.multiplyMM(mMVPMatrix,NO_OFFSET,mProjectionMatrix,NO_OFFSET,mMVPMatrix,NO_OFFSET)// 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系// 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变Matrix.scaleM(mMVPMatrix,NO_OFFSET,1f,-1f,1f,)}fun computeRotateModelMatrix(angleX: Float, axis: AXIS) {when (axis) {AXIS.X -> Matrix.rotateM(mModelMatrix, NO_OFFSET, angleX, 1f, 0f, 0f)AXIS.Y -> Matrix.rotateM(mModelMatrix, NO_OFFSET, angleX, 0f, 1f, 0f)AXIS.Z -> Matrix.rotateM(mModelMatrix, NO_OFFSET, angleX, 0f, 0f, 1f)}}fun computeScaleModelMatrix(scale: Float, axis: AXIS) {when (axis) {AXIS.X -> Matrix.scaleM(mModelMatrix, NO_OFFSET, scale, 1f, 1f)AXIS.Y -> Matrix.scaleM(mModelMatrix, NO_OFFSET, 1f, scale, 1f)else -> {}}}fun computeTranslateModelMatrix(translate: Float, axis: AXIS) {when (axis) {AXIS.X -> Matrix.translateM(mModelMatrix, NO_OFFSET, translate, 0f, 0f)AXIS.Y -> Matrix.translateM(mModelMatrix, NO_OFFSET, 0f, translate, 0f)else -> {}}}// 加载纹理fun loadTexture(context: Context, resourceId: Int): Int {val textureId = IntArray(1)// 生成纹理GLES30.glGenTextures(1, textureId, 0)// 绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId[0])// 设置纹理参数GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MIN_FILTER,GLES30.GL_LINEAR) // 纹理缩小时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MAG_FILTER,GLES30.GL_LINEAR) // 纹理放大时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充// 加载图片val options = BitmapFactory.Options().apply {inScaled = false // 不进行缩放}val bitmap = BitmapFactory.decodeResource(context.resources, resourceId, options)// 将图片数据加载到纹理中GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0)// 释放资源bitmap.recycle()// 解绑纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)Log.e("yang","loadTexture: 纹理加载成功 bitmap.width:${bitmap.width} bitmap.height:${bitmap.height}")return textureId[0]}fun enableTexture0(program: Int, id: Int) {GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, id)val textureSampleHandle = GLES30.glGetUniformLocation(program, "uTexture_0")if (textureSampleHandle != -1) {GLES30.glUniform1i(textureSampleHandle, 0)}}fun disableTexture0() {GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)}fun initTexture0(context: Context, resourceId: Int) {mTextureID[0] = loadTexture(context, resourceId)}// GLSurfaceView实时绘制fun drawCurrentOutput() {val state = saveGLState()try {GLES30.glUseProgram(mProgram)enableTexture0(mProgram, mTextureID[0])drawSomething(mProgram, mMVPMatrix)disableTexture0()} finally {restoreGLState(state)}}// 保存OpenGL状态private fun saveGLState(): GLState {val viewport = IntArray(4)val program = IntArray(1)val framebuffer = IntArray(1)GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewport, 0)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, program, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, framebuffer, 0)return GLState(viewport, program[0], framebuffer[0])}// 恢复OpenGL状态private fun restoreGLState(state: GLState) {GLES30.glViewport(state.viewport[0],state.viewport[1],state.viewport[2],state.viewport[3])GLES30.glUseProgram(state.program)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, state.framebuffer)}// OpenGL状态数据类data class GLState(val viewport: IntArray,val program: Int,val framebuffer: Int)object LoadShaderUtil {// 创建着色器对象fun loadShader(type: Int, source: String): Int {val shader = GLES30.glCreateShader(type)GLES30.glShaderSource(shader, source)GLES30.glCompileShader(shader)return shader}}
}

效果图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/74087.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Day78 | 灵神 | 反转链表 两两交换链表中的节点

Day78 | 灵神 | 反转链表 两两交换链表中的节点 24.两两交换链表中的节点 24. 两两交换链表中的节点 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 这道题就是下面这道题的k2的情况 25. K 个一组翻转链表 - 力扣&#xff08;LeetCode&#xff09; 基本思路和…

滤波---卡尔曼滤波

卡尔曼滤波概览 一、定义 卡尔曼滤波是一种基于线性系统和高斯噪声假设的递归最优状态估计算法。其核心目标是通过融合系统模型预测值与传感器测量值&#xff0c;在噪声环境中实时估计系统的动态状态&#xff08;如位置、速度、加速度等&#xff09;。 数学基础&#xff1a; …

23种设计模式-结构型模式-桥接器

文章目录 简介问题解决方案示例总结 简介 桥接器是一种结构型设计模式&#xff0c;可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构&#xff0c;从而能在开发时分别使用。 问题 假如你有一个几何形状Shape类&#xff0c;它有两个子类&#xff1a;圆形C…

手工排查后门木马的常用姿势

声明&#xff01;本文章所有的工具分享仅仅只是供大家学习交流为主&#xff0c;切勿用于非法用途&#xff0c;如有任何触犯法律的行为&#xff0c;均与本人及团队无关&#xff01;&#xff01;&#xff01; 1. 检查异常文件 &#xff08;1&#xff09;查找最近修改的文件 # 查…

工业机器人核心算法体系解析:从感知到决策的技术演进

工业机器人作为智能制造的核心装备,其技术竞争力的本质是算法体系的优化与创新。从静态轨迹执行到动态环境适应,从单一任务控制到复杂场景决策,工业机器人的算法体系涵盖环境感知、运动控制、路径规划、行为决策四大核心模块。本文将深入解析各模块的关键算法及其技术演进,…

当 EcuBus-Pro + UTA0401 遇上 NSUC1500

文章目录 1.前言2.EcuBus-Pro简介2.1 官方地址2.2 概览 3.纳芯微NSUC1500简介3.1 NSUC1500概述3.2 产品特性 4.测试环境5.基础功能5.1 数据发送5.2 数据监控 6.自动化功能6.1 脚本创建6.2 脚本编辑6.3 脚本编辑与测试 7.音乐律动7.1 导入例程7.2 效果展示 ECB工程 1.前言 最近…

说说Redis的内存淘汰策略?

大家好&#xff0c;我是锋哥。今天分享关于【说说Redis的内存淘汰策略?】面试题。希望对大家有帮助&#xff1b; 说说Redis的内存淘汰策略? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Redis的内存淘汰策略用于管理当内存达到最大限制时&#xff0c;如何处理过…

Python实现音频数字水印方法

数字水印技术可以将隐藏信息嵌入到音频文件中而不明显影响音频质量。下面我将介绍几种在Python中实现音频数字水印的方法。 方法一&#xff1a;LSB (最低有效位) 水印 import numpy as np from scipy.io import wavfile def embed_watermark_lsb(audio_path, watermark, ou…

Altium Designer 24 PCB 走线倒圆弧方法

Altium Designer 24 PCB 走线倒圆弧方法 问题描述解决方法设置倒圆弧参数选择需要优化的走线进行走线优化 优化效果展示 在 PCB 设计中&#xff0c;走线转角过于尖锐不仅影响美观&#xff0c;还可能引起信号完整性问题。本文介绍如何在 Altium Designer 24 中通过倒圆弧优化走线…

Cookie与Token详解及测试需重点关注点

在现代Web应用中&#xff0c;Cookie 和 Token 是两种常见的身份验证与会话管理机制。它们分别在不同的场景下扮演着重要的角色&#xff0c;在性能、灵活性和安全性方面具有各自的特点。作为测试人员&#xff0c;理解它们的工作原理以及如何对其进行有效的测试&#xff0c;是保证…

Unity 2022.3.x部分Android设备播放视频黑屏问题

Android平台视频兼容性问题很多…类似的黑屏问题真的很头大&#xff0c;总结一些常见问题&#xff1a; 1. 视频文件不支持压缩 如果使用AssetBundle加载视频&#xff0c;这个AssetBundle压缩格式要选None。有人可能会说最新版Unity已经支持bundle压缩下播放视频&#xff0c;稳…

Redis - 概述

目录 ​编辑 一、什么是redis 二、redis能做什么&#xff08;有什么特点&#xff09;&#xff1f; 三、redis有什么优势 四、Redis与其他key-value存储有什么不同 五、Redis命令 六、Redis数据结构 1、基础数据结构 2、高级数据结构 一、什么是redis 1、redis&#x…

数据库部署在服务器表不存在解决方案

MySQL 数据库表不存在错误解决方案 MySqlException (0x80004005): Table store.SysLogOperate doesnt exist 服务器用的mysql5.6 用这个表syslogoperate只是全是小写 看起来你在使用 Pomelo.EntityFrameworkCore.MySql 作为 MySQL 数据库的提供程序&#xff0c;并且在初始化…

图灵完备——游戏中进行实践

图灵完备 简述结构一、基本逻辑电路1、低电平2、高电平3、非门4、与门5、三路与门6、或门7、三路或门8、与非门9、或非门10、异或门11、同或门 二、算数运算&&存储器1、二进制速算2、成对的麻烦 简述 这周就要学习计算机组成原理了&#xff0c;为了学起来不那么吃力&am…

踏过强化学习的每一步推导

给定 l [ a n , . . . , a 0 ] l[a_n, ..., a_0] l[an​,...,a0​]&#xff0c;现在 for idx in range(len(l)-2, -1, -1):l[idx] l[idx1] * ld注&#xff1a;这里的ld就是 λ \lambda λ&#xff0c;定义 λ 0 1 \lambda^01 λ01 证明变换后&#xff1a; l [ ∑ i 0 n …

AI小白的第七天:必要的数学知识(概率)

概率 Probability 1. 概率的定义 概率是一个介于 0 和 1 之间的数&#xff0c;表示某个事件发生的可能性&#xff1a; 0&#xff1a;事件不可能发生。1&#xff1a;事件必然发生。0 到 1 之间&#xff1a;事件发生的可能性大小。 例如&#xff0c;掷一枚公平的硬币&#xf…

UE5 + Rider + VsCode 接入腾讯的 Puerts 脚本

学习了一段时间 U&#xff0c;写点啥就得等编译&#xff0c;体验真的是一言难尽。。。。。。 然后就想着给自己找个脚本好了&#xff0c;调研了一下 AngelScript&#xff0c;puerts 的可行性。 AngelScript 看着真的诱人&#xff0c;但是发现连官方提供的都是 UE 的预编译版本…

凸包构造算法—Graham 扫描法

1. 理论原理推导 核心思想 Graham 扫描法基于以下基本思想&#xff1a; 极角排序&#xff1a; 选取一个参考点&#xff08;通常选择 y 坐标最小的点&#xff0c;若存在多个&#xff0c;则选 x 坐标最小的&#xff09;&#xff0c;将其他点按照与该参考点构成的极角进行升序排…

如何在 Windows 上安装与配置 Tomcat

Apache Tomcat 是一个开源的 Servlet 容器和 Web 服务器&#xff0c;广泛用于 Java Web 应用的开发和部署。它是实现 Java EE&#xff08;现称 Jakarta EE&#xff09;规范中的 Servlet 和 JSP 的官方参考实现。在本文中&#xff0c;我们将详细介绍如何在 Windows 系统上安装并…

测试模版15

本篇技术博文摘要 &#x1f31f; 引言 &#x1f4d8; 在这个变幻莫测、快速发展的技术时代&#xff0c;与时俱进是每个IT工程师的必修课。我是盛透侧视攻城狮&#xff0c;一名什么都会一丢丢的网络安全工程师&#xff0c;也是众多技术社区的活跃成员以及多家大厂官方认可人员&a…