当前位置: 首页 > news >正文

如何以JNI方式实现安卓APP控制GPIO?

本文档提供了在 Android 10 设备上通过应用程序(App)控制通用输入输出(GPIO)的详细指南。这涵盖了从创建 gpio驱动到App 配置 以及 SELinux 策略以允许特定访问的所有必要步骤。

1. 驱动实现

添加创建gpio控制驱动bsp\kernel\kernel4.14\drivers\gpio\gpio_led.c,并添加好对应的Makfile编译

#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/ioctl.h>#include <linux/uaccess.h>#include <linux/string.h>#include <linux/wait.h>#include <linux/types.h>#include <linux/proc_fs.h>#include <linux/of.h>#include <linux/of_gpio.h>#include <linux/gpio.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/err.h>#include <linux/gpio/consumer.h>#include <linux/io.h>#include <linux/miscdevice.h>#include <linux/irq.h>#include <linux/of_irq.h>#include <linux/kernel.h>#include <linux/dmi.h>#include <linux/firmware.h>#include <linux/gpio/consumer.h>#include <linux/input.h>#include <linux/input/mt.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/irq.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/acpi.h>#include <linux/of.h>#include <asm/unaligned.h>#define GPIO_HIGH _IO('L', 0)#define GPIO_LOW _IO('L', 1)#define LED_ON 1#define LED_OFF 0#define SIMPIE_LED_MAX 4//============================== Upper interface value ==============================//// 驱动模块名称定义#define MODULE_NAME "gpio_led"          // 驱动模块的名字#define MISC_NAME "gpio_led_device"     // 用于注册为“misc”设备的名字// 模块函数接口定义,供上层应用调用的接口。通过MM_DEV_MAGIC区分不同系统接口,通过_IO()加上自己的编号作为接口number。#define MM_DEV_MAGIC 'N'// LED 控制命令#define RFID_IO1 _IO(MM_DEV_MAGIC, 93)#define RFID_IO2 _IO(MM_DEV_MAGIC, 130)#define RFID_IO3 _IO(MM_DEV_MAGIC, 121)#define RFID_LED _IO(MM_DEV_MAGIC, 138)static int major;static struct class *cls;// GPIO 描述数组struct gpio_desc *led_gpio[SIMPIE_LED_MAX];// cat命令将调用该函数static ssize_t gpio_value_show(struct device *dev, struct device_attribute *attr, char *buf){return sprintf(buf, "%d\n", gpiod_get_value(led_gpio[0]));}// echo命令将调用该函数static ssize_t gpio_value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){pr_err("[vanxoak]%c\n", buf[0]);if ('0' == buf[0]){gpiod_direction_output(led_gpio[0], 0);pr_err("[vanxoak]: _%s_ :gpio off\n", __func__);}else if ('1' == buf[0]){gpiod_direction_output(led_gpio[0], 1);pr_err("[vanxoak]: _%s_ :gpio on\n", __func__);}elsepr_err("I only support 0 or 1 to ctrl gpio on or off\n");pr_err("[vanxoak]gpio_value_store\n");return len;}// 定义一个名为gpio_led的设备属性static DEVICE_ATTR(gpio_led, 0664, gpio_value_show, gpio_value_store);// 提供给上层控制的接口long gpio_led_ioctl(struct file *file, unsigned int cmd, unsigned long arg){switch (cmd){case RFID_LED:gpiod_direction_output(led_gpio[0], arg);break;case RFID_IO1:gpiod_direction_output(led_gpio[1], arg);break;case RFID_IO2:gpiod_direction_output(led_gpio[2], arg);break;case RFID_IO3:gpiod_direction_output(led_gpio[3], arg);break;default:pr_err("[vanxoak] %s default: break\n", __func__);break;}return 0;}struct file_operations gpio_led_ops = {.owner = THIS_MODULE,.unlocked_ioctl = gpio_led_ioctl,};// LED灯初始化static int simpie_led_init(struct platform_device *pdev){int ret = 0;int i;// 申请gpio设备led_gpio[0] = devm_gpiod_get(&pdev->dev, "led0", GPIOD_OUT_LOW);led_gpio[1] = devm_gpiod_get(&pdev->dev, "led1", GPIOD_OUT_LOW);led_gpio[2] = devm_gpiod_get(&pdev->dev, "led2", GPIOD_OUT_LOW);led_gpio[3] = devm_gpiod_get(&pdev->dev, "led3", GPIOD_OUT_LOW);for (i = 0; i < SIMPIE_LED_MAX; i++){if (IS_ERR(led_gpio[i])){ret = PTR_ERR(led_gpio[i]);return ret;}// 输出初始电平ret = gpiod_direction_output(led_gpio[i], LED_OFF);}device_create_file(&pdev->dev, &dev_attr_gpio_led);return ret;}// 驱动入口static int gpio_led_probe(struct platform_device *pdev){int ret = 0;pr_err("[vanxoak]gpio_led_probe start...\n");// LED灯gpio初始化及输出配置ret = simpie_led_init(pdev);pr_err("[vanxoak]gpio_led_probe end...\n");return 0;}// 绑定设备static struct of_device_id gpio_led_match_table[] = {{.compatible = "yz,gpio-led"},{}};static int gpio_led_remove(struct platform_device *pdev){pr_err("[vanxoak]gpio_led_remove...\n");return 0;}static struct platform_driver gpio_led_driver = {.driver = {.name = MODULE_NAME,.owner = THIS_MODULE,.of_match_table = gpio_led_match_table,},.probe = gpio_led_probe,.remove = gpio_led_remove,};// gpio初始化入口static int gpio_led_init(void){struct device *mydev;pr_err("[vanxoak]gpio_led_init start...\n");platform_driver_register(&gpio_led_driver);major = register_chrdev(0, "gpiotest", &gpio_led_ops);// 创建gpio_led_class设备cls = class_create(THIS_MODULE, "gpio_led_class");// 在gpio_led_class设备目录下创建一个gpio_led_device属性文件mydev = device_create(cls, 0, MKDEV(major, 0), NULL, MISC_NAME);if (sysfs_create_file(&(mydev->kobj), &dev_attr_gpio_led.attr)){return -1;}return 0;}static void gpio_led_exit(void){pr_err("[vanxoak]gpio_led_exit...\n");platform_driver_unregister(&gpio_led_driver);device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, "gpiotest");}module_init(gpio_led_init);module_exit(gpio_led_exit);MODULE_DESCRIPTION("Device_create Driver");MODULE_LICENSE("GPL");

设备树配置 

gpio_led: yz,gpio-led {status = "disabled";compatible = "yz,gpio-led";led0-gpio = <&ap_gpio 138 GPIO_ACTIVE_HIGH>;led1-gpio = <&ap_gpio 93 GPIO_ACTIVE_HIGH>;led2-gpio = <&ap_gpio 130 GPIO_ACTIVE_HIGH>;led3-gpio = <&ap_gpio 121 GPIO_ACTIVE_HIGH>;};

配置好上面gpio驱动后重新编译更新kernel 可以在/dev目录下找到对应的设备文件

"/dev/gpio_led_device",通过读写设备文件就可以操作gpio了。

1.2 创建 Native 库

1.2.1设置 JNI 方法

在 App 中定义 JNI 方法以实现与 GPIO 设备的交互。

public class NativeClass {static {try {System.loadLibrary("jni_gpiocontrol");Log.d("NativeClass", "Native library loaded successfully.");} catch (UnsatisfiedLinkError e) {Log.e("NativeClass", "Failed to load native library: " + e.getMessage());// throw new RuntimeException("Failed to load native library", e);}}// 声明本地方法public native int controlGPIO(int cmd, long arg);
}
1.2.2 实现 Native 方法

在app/src/main目录下创建一个cpp文件夹(如果你的项目是用Kotlin编写的,这个步骤仍然适用,因为JNI是用C/C++实现的)。将你的libjni_gpiocontrol.cpp文件放到这个cpp目录中。

注意事项:确保本地方法签名正确,Java方法签名和本地(C/C++)方法实现之间必须完全匹配。

#include <jni.h>#include <fcntl.h>#include <unistd.h>#include <sys/ioctl.h>#include <stdio.h>#include <android/log.h>#include <errno.h>#include <string.h>#define MM_DEV_MAGIC 'N'#define RFID_LED _IO(MM_DEV_MAGIC, 138)#define RFID_IO1 _IO(MM_DEV_MAGIC, 93)#define RFID_IO2 _IO(MM_DEV_MAGIC, 130)#define RFID_IO3 _IO(MM_DEV_MAGIC, 121)#define DEVICE_PATH "/dev/gpio_led_device"#define LOG_TAG "GPIOControl"extern "C" JNIEXPORT jint JNICALLJava_com_example_gpio_NativeClass_controlGPIO(JNIEnv *env, jobject obj, jint cmd, jlong arg) {int device_fd;long ioctl_result;unsigned int ioctl_cmd = cmd;// Open the device filedevice_fd = open(DEVICE_PATH, O_RDWR);if (device_fd < 0) {__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Could not open device: %s", strerror(errno));return -1;}// Translate cmd to appropriate ioctl command based on inputswitch (cmd) {case 138:ioctl_cmd = RFID_LED;break;case 93:ioctl_cmd = RFID_IO1;break;case 130:ioctl_cmd = RFID_IO2;break;case 121:ioctl_cmd = RFID_IO3;break;default:__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Invalid command");close(device_fd);return -1;}// Send an ioctl to the deviceioctl_result = ioctl(device_fd, ioctl_cmd, arg);if (ioctl_result < 0) {__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to call ioctl: %s", strerror(errno));close(device_fd);return -1;}// Close the deviceclose(device_fd);return 0;}

1.2.3 编译 Native 库

使用 CMake 或 ndk-build 工具编译你的 native 代码为共享库(.so 文件)。

添加app\src\main\cpp\CMakeLists.txt 

cmake_minimum_required(VERSION 3.4.1)project("gpiotest")add_library(jni_gpiocontrol SHARED libjni_gpiocontrol.cpp)find_library( log-lib log )target_link_libraries(jni_gpiocontrol${log-lib} ) 
1.2.4 调用 Native 方法

通过 JNI 接口在 App 中调用实现的 native 方法以控制 GPIO。

public class MainActivity extends AppCompatActivity {private NativeClass nativeClass;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);nativeClass = new NativeClass();// 示例:打开LEDint result = nativeClass.controlGPIO(138, 1);// 根据result处理结果}}

2. SELinux 配置

由于直接访问硬件设备在 Android 中受到 SELinux 策略的限制,需要修改 SELinux 策略以允许 App 访问 GPIO 设备文件。

定义设备类型:为 GPIO 设备定义一个新的 SELinux 类型(如 gpio_led_device_t)。

在SDK_dir/device/sprd/sharkle/common/sepolicy/device.te 添加

# 定义新的设备类型type gpio_led_device_t, dev_type;

分配文件上下文:为 GPIO 设备文件分配新定义的 SELinux 类型。

SDK_dir/device/sprd/sharkle/common/sepolicy/file_contexts中添加

/dev/gpio_led_device u:object_r:gpio_led_device_t:s0

授予权限:在 SELinux 策略中添加规则,允许 App 访问 GPIO 设备。

SDK_dir/device/sprd/sharkle/common/sepolicy/system_app.te

# 允许 system_app 访问 gpio_led_deviceallow system_app gpio_led_device_t:chr_file { read write };

重新编译 SELinux 策略:对更改的 SELinux 策略进行编译,并将其部署到设备上。这一步骤的目的是将自定义的安全策略更改应用到Android构建系统的预设SELinux策略中,确保在编译系统镜像时,这些更改会被包含进去。

cp system/sepolicy/public/app.te system/sepolicy/prebuilts/api/29.0/public/app.tecp system/sepolicy/private/coredomain.te system/sepolicy/prebuilts/api/29.0/private/coredomain.te

3. 测试与部署

测试 App:在具有所需硬件支持的 Android 10 设备上测试 App。确保 App 能成功加载 native 库,并能通过 JNI 调用控制 GPIO。

SELinux 策略测试:验证 SELinux 策略更改是否允许 App 无障碍地访问 GPIO 设备。

问题排查:如果遇到访问被拒绝的情况,请检查 SELinux 审计日志以确定是否需要进一步调整策略。

3.1注意事项

安全性:在修改 SELinux 策略以增加访问权限时,务必小心谨慎,避免引入安全漏洞。

设备兼容性:确保你的实现考虑到了不同设备可能存在的硬件和配置差异。

文档和维护:适当记录你的设计和实现过程,包括 JNI 接口、native 代码和 SELinux 策略更改,以便于未来的审计和维护。

通过遵循以上步骤,你可以在遵守 Android 安全模型的同时,实现 App 对 GPIO 的有效控制。

相关文章:

如何以JNI方式实现安卓APP控制GPIO?

本文档提供了在 Android 10 设备上通过应用程序&#xff08;App&#xff09;控制通用输入输出&#xff08;GPIO&#xff09;的详细指南。这涵盖了从创建 gpio驱动到App 配置 以及 SELinux 策略以允许特定访问的所有必要步骤。 1. 驱动实现 添加创建gpio控制驱动bsp\kernel\ke…...

计算机网络学习笔记——运输层(b站)

目录 一、 运输层概述 二、运输层端口号、复用与分用的概念 三、UDP和TCP的对比 四、TCP的流量控制 五、TCP的拥塞控制 六、TCP超时重传时间的选择 七、TCP可靠传输的实现 八、TCP报文段的首部格式 一、 运输层概述 物理层、数据链路层、网络层实现了主机到主机的通信…...

HBase数据库面试知识点:第二部分 - 核心技术(持续更新中)

目录 1. 分布式存储与HDFS 2. 面向列的存储 3. 数据版本控制 4. Region与RegionServer 5. 分布式协调服务&#xff08;ZooKeeper&#xff09; 1. 分布式存储与HDFS HBase利用Hadoop的HDFS作为其底层存储系统&#xff0c;确保数据的高可靠性和可扩展性。 数据块&#xff0…...

Spring 使用SSE(Server-Sent Events)学习

什么是SSE SSE 即服务器发送事件&#xff08;Server-Sent Events&#xff09;&#xff0c;是一种服务器推送技术&#xff0c;允许服务器在客户端建立连接后&#xff0c;主动向客户端推送数据。 SSE 基于 HTTP 协议&#xff0c;使用简单&#xff0c;具有轻量级、实时性和断线重…...

词法分析器的设计与实现--编译原理操作步骤,1、你的算法工作流程图; 2、你的函数流程图;3,具体代码

实验原理&#xff1a; 词法分析是编译程序进行编译时第一个要进行的任务&#xff0c;主要是对源程序进行编译预处理之后&#xff0c;对整个源程序进行分解&#xff0c;分解成一个个单词&#xff0c;这些单词有且只有五类&#xff0c;分别时标识符、关键字&#xff08;保留字&a…...

linux查看磁盘类型命令

在Linux中&#xff0c;有多种方法可以查看磁盘是固态硬盘&#xff08;SSD&#xff09;还是机械硬盘&#xff08;HDD&#xff09;。以下是一些常用的方法&#xff1a; 查看/sys/block/目录 /sys/block/目录包含了系统中所有块设备的信息。你可以查看这个目录中的设备属性来判断…...

多线程调用同一个不包含可变状态,并且是线程安全的方法时,可同时执行,不必等待排队

多线程调用同一个不包含可变状态&#xff0c;并且是线程安全的方法时&#xff0c;可同时执行&#xff0c;不必等待排队 前言同时执行方法的条件示例并发执行的优势实验验证总结 前言 如果方法不包含可变状态&#xff0c;并且是线程安全的&#xff0c;那么在高并发环境下&#…...

Java文件操作①——XML文件的读取

系列文章目录 文章目录 系列文章目录前言一、邂逅XML二、应用 DOM 方式解析 XML三、应用 SAX 方式解析 XML四、应用 DOM4J 及 JDOM 方式解析 XMLJDOM 方式解析 XMLDOM4J 方式解析 XML前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。…...

【记录】网络|没有路由器没有网线,分别使用手机或Windows电脑共享网络给ARM64开发板,应急连接

事情是这样的&#xff0c;我的开发板明明已经选择了记住热点 WiFi 密码&#xff0c;但是却没有在开机的时候自动连接&#xff0c;我又没有放显示器在身边&#xff0c;又不想为了这点事去找个显示器来&#xff0c;就非常难受。 我手边有的设备是&#xff1a; 笔记本电脑&#…...

一键设置常用纸张和页面边距-Word插件-大珩助手

Word大珩助手是一款功能丰富的Office Word插件&#xff0c;旨在提高用户在处理文档时的效率。它具有多种实用的功能&#xff0c;能够帮助用户轻松修改、优化和管理Word文件&#xff0c;从而打造出专业而精美的文档。 【新功能】常用纸张和常用边距 1、一键设定符合中国人常用…...

在树莓派3B+中下载opencv(遇到的各种问题及解决)

目录 前言 1、删除原版本下新版本 2、python虚拟环境 3、python版本共存换链接——给版本降低 4、烧录之前版本的文件&#xff08;在清华源中可以找&#xff0c;不用官网的烧录文件就行&#xff1b; 比如&#xff1a;&#xff08;balenaEtcher&#xff09;重新烧录有问题…...

精准检测,安全无忧:安全阀检测实践指南

安全阀作为一种重要的安全装置&#xff0c;在各类工业系统和设备中发挥着举足轻重的作用。 它通过自动控制内部压力&#xff0c;有效防止因压力过高而引发的设备损坏和事故风险&#xff0c;因此&#xff0c;对安全阀进行定期检测&#xff0c;确保其性能完好、工作可靠&#xf…...

Transformer系列:图文详解KV-Cache,解码器推理加速优化

前言 KV-Cache是一种加速Transformer推理的策略&#xff0c;几乎所有自回归模型都内置了KV-Cache&#xff0c;理解KV-Cache有助于更深刻地认识Transformer中注意力机制的工作方式。 自回归推理过程知识准备 自回归模型采用shift-right的训练方式&#xff0c;用前文预测下一个…...

基础篇03——SQL约束

概述 约束示例 完成以下案例&#xff1a; create table user (id int primary key auto_increment comment 主键,name varchar(10) not null unique comment 姓名,age tinyint unsigned check ( age > 0 and age < 120 ) comment 年龄,status char(1) default 1 commen…...

人工智能--深度神经网络

目录 &#x1f349;引言 &#x1f349;深度神经网络的基本概念 &#x1f348;神经网络的起源 &#x1f34d; 神经网络的基本结构 &#x1f349;深度神经网络的结构 &#x1f348; 卷积神经网络&#xff08;CNN&#xff09; &#x1f348;循环神经网络&#xff08;RNN&…...

VOC格式标签各个字段的解释

想了解一下VOC格式数据标签各个字段的含义&#xff0c;搜了一圈没看到&#xff0c;懒得去官网了&#xff0c;直接问了GPT-4o&#xff0c;以下回答字段解析来自GPT-4o&#xff0c;例子我自己写的 VOC (Visual Object Classes) 数据标签格式主要用于目标检测任务。VOC格式的标签…...

2024年端午节放假通知

致尊敬的客户以及全体同仁&#xff1a; 2024年端午节将至&#xff0c;根据国务院办公厅通知精神&#xff0c;结合公司的实际情况&#xff0c;现将放假事宜通知如下&#xff1a; 2024年6月8日&#xff08;星期六&#xff09;至6月10日&#xff08;星期一&#xff09;&#xff…...

Transformer系列:注意力机制的优化,MQA和GQA原理简述

前言 多查询注意力(MQA)、分组查询注意力(GQA)是Transformer中多头注意力(MHA)的变种&#xff0c;它们大幅提高了解码器的推理效率&#xff0c;在LLaMA-2&#xff0c;ChatGLM2等大模型中有广泛使用&#xff0c;本篇介绍MQA、GQA的原理并分析其源码实现。 使用MQA&#xff0c;G…...

Python知识点11---高阶函数

提前说一点&#xff1a;如果你是专注于Python开发&#xff0c;那么本系列知识点只是带你入个门再详细的开发点就要去看其他资料了&#xff0c;而如果你和作者一样只是操作其他技术的Python API那就足够了。 本篇介绍一下Python的内置函数也叫高阶函数&#xff0c;就是Python自…...

JavaSE——【逻辑控制】(习题)

一、分支结构 2.1 if 语句 【练习】2.1.1 小明&#xff0c;如果这次考到90分以上&#xff0c;给你奖励一个大鸡腿&#xff0c;否则奖你一个大嘴巴子 int score 92;if(score > 90){System.out.println("吃个大鸡腿!!!");}else{System.out.println("挨大嘴…...

3步实现Web界面设计标注高效交付:面向全栈团队的Sketch Measure应用指南

3步实现Web界面设计标注高效交付&#xff1a;面向全栈团队的Sketch Measure应用指南 【免费下载链接】sketch-measure Make it a fun to create spec for developers and teammates 项目地址: https://gitcode.com/gh_mirrors/sk/sketch-measure 在Web开发项目中&#x…...

中兴光猫配置解密工具:3步解锁家庭网络自主权

中兴光猫配置解密工具&#xff1a;3步解锁家庭网络自主权 【免费下载链接】ZET-Optical-Network-Terminal-Decoder 项目地址: https://gitcode.com/gh_mirrors/ze/ZET-Optical-Network-Terminal-Decoder 你是否曾经因为无法修改光猫配置而感到束手无策&#xff1f;当网…...

Qwen3-14B推理速度实测:10核CPU+24GB显存下首token延迟<800ms

Qwen3-14B推理速度实测&#xff1a;10核CPU24GB显存下首token延迟<800ms 1. 测试环境与配置 1.1 硬件配置 本次测试使用的硬件配置完全匹配Qwen3-14B私有部署镜像的推荐规格&#xff1a; GPU&#xff1a;RTX 4090D 24GB显存&#xff08;NVIDIA驱动550.90.07&#xff09;…...

啪」的一声脆响,空气击穿时那道紫色电弧总能让人心头一紧。咱们今天用COMSOL做个好玩的——计算两根针尖电极间的击穿电压,看看电场怎么在金属尖角处「拧麻花

comsol放电电极击穿空气模拟&#xff0c;计算击穿间隙的电压&#xff0c;周围附近的电场老规矩&#xff0c;先画个直径10mm的球头圆柱电极&#xff0c;对面放个尖角曲率半径0.1mm的针电极&#xff0c;间隙留5mm。材料库选「空气」&#xff0c;但要注意击穿模型得用自定义的。物…...

手把手教你解决HarmonyOS项目中的hvigor版本冲突问题(含API8/9兼容方案)

HarmonyOS开发实战&#xff1a;彻底解决hvigor版本冲突与API兼容性问题 上周团队新来的工程师小王在调试P40设备时突然惊呼&#xff1a;"这报错太诡异了&#xff01;明明代码没问题&#xff0c;为什么安装包死活装不上&#xff1f;"我凑近一看&#xff0c;控制台正显…...

思考时爱用手托腮?警惕单侧发力拖垮颈肩平衡

很多人在工作、学习或思考时&#xff0c;习惯用手托腮&#xff0c;这个看似不经意的动作&#xff0c;会给颈肩带来持续负担&#xff0c;引发肌肉失衡劳损。用手托腮时&#xff0c;头部会向一侧倾斜&#xff0c;颈椎处于侧屈状态&#xff0c;颈部一侧肌肉持续紧张、牵拉&#xf…...

B站视频字幕抓取实战:Tampermonkey搭配GreasyFork脚本,5分钟搞定CC字幕导出

B站视频字幕高效提取指南&#xff1a;Tampermonkey与GreasyFork脚本深度应用 每次观看B站优质内容时&#xff0c;那些精心制作的字幕是否让你想保存下来反复学习&#xff1f;传统录屏或手动抄写效率低下&#xff0c;而专业工具又过于复杂。本文将带你探索浏览器脚本的魔法世界&…...

一张照片秒变3D模型!用Splatter Image和3D高斯溅射快速上手单视图重建

从单张照片到3D模型&#xff1a;Splatter Image技术实战指南 想象一下&#xff0c;你刚在二手市场淘到一个绝版手办&#xff0c;想为它创建数字档案&#xff1b;或是设计师客户临时需要将一张产品照片转为3D模型。传统流程需要专业设备扫描或手工建模&#xff0c;耗时数小时甚…...

立创·地阔星开发板开箱测评:除了点灯,STM32F103C8T6还能怎么玩?(附资源下载与避坑指南)

立创地阔星开发板深度探索&#xff1a;从开箱到创意项目实战 拆开快递包装的那一刻&#xff0c;这块蓝色PCB板安静地躺在防静电袋里——这就是最近在创客圈备受关注的立创地阔星开发板。作为一款基于STM32F103C8T6芯片的高性价比开发平台&#xff0c;它不仅适合初学者入门&…...

预制指标、宽表、SQL、本体ABC:真正决定长期成本的,是一次变更会波及多少层

企业做智能问数&#xff0c;最常见的比较题是&#xff1a;预制指标、宽表、人工 SQL、本体ABC&#xff0c;到底哪条路线维护成本更低&#xff1f;如果只给一个笼统答案&#xff0c;往往容易失真。因为真正决定长期成本的&#xff0c;不是“今天开发快不快”&#xff0c;也不是“…...