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

安卓开发相机功能

相机功能

安卓中的相机调用功能也经历了很多的方案升级,目前可选的官方方案是CameraX、Camera2、Camera(废弃),还有一些第三方免费或者是付费的相机库。对于大多数开发者,建议使用 CameraX。

CameraX

CameraX 是 Android Jetpack 库的一部分,旨在简化相机应用的开发。它构建在 Camera2 API 之上,为开发者提供了更简洁的接口,相比于Camera和Camera2,有更好的设备兼容性。

  • 简单易用:相比 Camera2,CameraX 简化了相机操作,提供了更直观的 API,可以更快实现常见的相机功能。
  • 向后兼容性:CameraX 支持 Android 5.0 (API 21) 及以上版本,解决了 Camera2 在一些设备上的兼容性问题。
  • 生命周期感知:CameraX 会自动处理生命周期问题,例如当用户切换到后台时停止相机,回到前台时重新启动。
  • 内置扩展:CameraX 提供了诸如 HDR、夜间模式、美颜等功能,支持基于不同设备硬件的特性自动调整。不过这个需要看手机型号,很多手机并不支持。

CameraX拍照

  1. 项目依赖配置

在 build.gradle 文件中添加 CameraX 相关依赖:

def camerax_version = "1.2.0-alpha04" implementation "androidx.camera:camera-core:$camerax_version"//核心库 implementation "androidx.camera:camera-camera2:$camerax_version"//基于 Camera2 的实现模块 implementation "androidx.camera:camera-lifecycle:$camerax_version"//自动管理相机的生命周期 implementation "androidx.camera:camera-view:1.0.0-alpha31"//显示相机预览的 UI 组件 implementation "androidx.camera:camera-extensions:1.0.0-alpha31"//额外的高级相机功能如 HDR 和美颜

  1. 权限配置(AndroidManifest.xml)

运行相机需要对相机权限做出声明:

<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" android:required="false" />

android:required="false":这意味着相机功能并不是应用的必需条件。如果设备没有相机,应用仍然可以安装和运行。如果是true,而设备本身没有相机,那应用就无法正常运行。

  1. 布局文件(activity_main.xml)

<!-- CameraX 预览控件 --> <androidx.camera.view.PreviewView android:id="@+id/previewView" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@+id/captureButton" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> <!-- 拍照按钮 --> <Button android:id="@+id/captureButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="拍照" app:layout_constraintTop_toBottomOf="@+id/previewView" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" />

  1. Activity中

package com.example.cameraxdemo; import android.Manifest; import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; import android.widget.Button; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.camera.core.Camera; import androidx.camera.core.CameraSelector; import androidx.camera.core.ImageCapture; import androidx.camera.core.ImageCaptureException; import androidx.camera.core.Preview; import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.camera.view.PreviewView; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import com.google.common.util.concurrent.ListenableFuture; import java.io.File; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MainActivity extends AppCompatActivity { private static final int REQUEST_CODE_PERMISSIONS = 10; private static final String[] REQUIRED_PERMISSIONS = new String[]{Manifest.permission.CAMERA}; private PreviewView previewView; private ImageCapture imageCapture; private ExecutorService cameraExecutor; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); previewView = findViewById(R.id.previewView); Button captureButton = findViewById(R.id.captureButton); // 请求相机权限,如果有权限直接启动相机 if (allPermissionsGranted()) { startCamera(); } else { ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS); } // 点击拍照按钮时执行拍照 captureButton.setOnClickListener(view -> takePhoto()); cameraExecutor = Executors.newSingleThreadExecutor(); } // 初始化相机 private void startCamera() { ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); cameraProviderFuture.addListener(() -> { try { // 获取 CameraProvider ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); // 创建预览 Preview preview = new Preview.Builder().build(); // 创建 ImageCapture,用于拍照 imageCapture = new ImageCapture.Builder().build(); // 选择后置摄像头 CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; // 将预览与 PreviewView 绑定 preview.setSurfaceProvider(previewView.getSurfaceProvider()); // 绑定预览和 ImageCapture 到相机生命周期 cameraProvider.unbindAll(); Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture); } catch (ExecutionException | InterruptedException e) { Log.e("CameraXDemo", "Error starting camera: ", e); } }, ContextCompat.getMainExecutor(this)); } // 拍照逻辑 private void takePhoto() { if (imageCapture == null) { return; } // 创建保存文件 File photoFile = new File(getExternalFilesDir(null), System.currentTimeMillis() + ".jpg"); // 设置拍照输出选项 ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(photoFile).build(); // 执行拍照 imageCapture.takePicture(outputFileOptions, cameraExecutor, new ImageCapture.OnImageSavedCallback() { @Override public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) { runOnUiThread(() -> Toast.makeText(MainActivity.this, "Photo saved: " + photoFile.getAbsolutePath(), Toast.LENGTH_SHORT).show()); } @Override public void onError(@NonNull ImageCaptureException exception) { Log.e("CameraXDemo", "Photo capture failed: " + exception.getMessage(), exception); } }); } // 检查是否已经获得所有权限 private boolean allPermissionsGranted() { for (String permission : REQUIRED_PERMISSIONS) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { return false; } } return true; } // 权限请求结果回调 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CODE_PERMISSIONS) { if (allPermissionsGranted()) { startCamera(); } else { Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show(); finish(); } } } @Override protected void onDestroy() { super.onDestroy(); cameraExecutor.shutdown(); } }

使用MediaStore,遵循分区存储:

// 创建 ContentValues ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, System.currentTimeMillis() + ".jpg"); contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/CameraXDemo"); // 设置输出选项 ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(getContentResolver(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues).build(); // 拍照 imageCapture.takePicture(outputFileOptions, cameraExecutor, new ImageCapture.OnImageSavedCallback() { @Override public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) { Uri savedUri = outputFileResults.getSavedUri(); if (savedUri != null) { runOnUiThread(() -> Toast.makeText(CameraActivity.this, "Photo saved to MediaStore: " + savedUri, Toast.LENGTH_SHORT).show()); } else { Log.e(TAG, "Image not saved properly."); } } @Override public void onError(@NonNull ImageCaptureException exception) { Log.e(TAG, "Photo capture failed: " + exception.getMessage(), exception); } });

切换前后置摄像头

主要思路是重新绑定摄像头,在重新绑定之前切换前后摄像头参数即可:

public class MainActivity extends AppCompatActivity { private PreviewView previewView; private ImageCapture imageCapture; private ProcessCameraProvider cameraProvider; private CameraSelector cameraSelector; private boolean isFrontCamera = false; // 默认后置摄像头 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); previewView = findViewById(R.id.previewView); // 启动相机 startCamera(); // 切换摄像头按钮点击事件 findViewById(R.id.switchCameraButton).setOnClickListener(v -> { isFrontCamera = !isFrontCamera; switchCamera(); }); } private void startCamera() { // 获取 CameraProvider ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); cameraProviderFuture.addListener(() -> { try { cameraProvider = cameraProviderFuture.get(); // 默认使用后置摄像头 cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; bindCameraUseCases(); } catch (ExecutionException | InterruptedException e) { e.printStackTrace(); } }, ContextCompat.getMainExecutor(this)); } private void bindCameraUseCases() { // 创建预览 Preview preview = new Preview.Builder().build(); // 将 Preview 连接到 PreviewView preview.setSurfaceProvider(previewView.getSurfaceProvider()); // 拍照设置 imageCapture = new ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build(); // 解绑之前的所有用例 cameraProvider.unbindAll(); // 绑定预览和拍照功能到相机 cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, preview, imageCapture); } private void switchCamera() { // 切换前后置摄像头 if (isFrontCamera) { cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA; } else { cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; } // 重新绑定摄像头 bindCameraUseCases(); } // 拍照方法 private void takePicture() { if (imageCapture != null) { ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "my_image_" + System.currentTimeMillis() + ".jpg"); contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(getContentResolver(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues).build(); imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() { @Override public void onImageSaved(ImageCapture.OutputFileResults outputFileResults) { Uri savedUri = outputFileResults.getSavedUri(); Log.d("CameraXApp", "Image saved: " + savedUri); } @Override public void onError(ImageCaptureException exception) { Log.e("CameraXApp", "Error saving image: " + exception.getMessage()); } }); } } }

CameraX视频拍摄

  1. 项目依赖配置

除了CameraX的基础依赖库,还需要新增下面的库:

implementation "androidx.camera:camera-video:$camerax_version" // 视频录制相关库

  1. 权限配置

录制视频,除了需要获取相机权限,还需要额外添加录音权限

<uses-permission android:name="android.permission.RECORD_AUDIO" />

  1. Activity中

public class VideoRecordActivity extends AppCompatActivity { private static final String TAG = "VideoRecordActivity"; private PreviewView previewView;// 预览摄像头捕获内容的视图 private ExecutorService cameraExecutor;// 用于处理相机操作的后台线程 private boolean isRecording; // 记录否正在录制的状态 private VideoCapture<Recorder> videoCapture; //捕获视频的核心组件 // private Recording recording; private ProcessCameraProvider cameraProvider;// 相机的生命周期管理组件 private CameraSelector cameraSelector;// 前置或后置摄像头 private Recording recording;//当前正在进行的录制实例 private ImageView ivRecord; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_record); previewView = findViewById(R.id.preview); //拍照 ivRecord = findViewById(R.id.tv_take); ivRecord.setOnClickListener(view -> { // 如果当前正在录制,点击按钮停止录制,否则就是开始录制 if (isRecording) { stopRecording(); } else { startRecording(); } }); // 创建单线程后台线程池 cameraExecutor = Executors.newSingleThreadExecutor(); startCamera(); requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 11); } // 启动相机预览 private void startCamera() { ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); // 异步获取摄像头的命周期管理器实例 cameraProviderFuture.addListener(() -> { try { // 获取摄像头生命周期管理器实例 cameraProvider = cameraProviderFuture.get(); // 创建预览实例 Preview preview = new Preview.Builder().build(); // 将预览内容绑定到 PreviewView 上 preview.setSurfaceProvider(previewView.getSurfaceProvider()); // 视频录制用例 Recorder recorder = new Recorder.Builder() .setQualitySelector(QualitySelector.from(Quality.HD)) // 设置录制质量为 HD .build(); // 创建 VideoCapture 用例,这个一个获取视频的核心组件 videoCapture = VideoCapture.withOutput(recorder); // 默认使用后置摄像头 cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; //解绑所有之前的摄像头用例 cameraProvider.unbindAll(); // 将预览和视频录制绑定到生命周期 cameraProvider.bindToLifecycle(VideoRecordActivity.this, cameraSelector, preview, videoCapture); } catch (ExecutionException | InterruptedException e) { Log.e(TAG, "Error starting camera", e); } }, ContextCompat.getMainExecutor(this));// 在主线程执行 } private void startRecording() { if (isRecording) { Toast.makeText(this, "Recording is already in progress", Toast.LENGTH_SHORT).show(); return; } // 创建保存视频的 ContentValues,指定文件名和文件类型 ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "video_" + System.currentTimeMillis()); contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4"); // 使用 MediaStoreOutputOptions 指定输出位置 MediaStoreOutputOptions options = new MediaStoreOutputOptions.Builder(getContentResolver(), MediaStore.Video.Media.EXTERNAL_CONTENT_URI) .setContentValues(contentValues).build(); // 准备录制前检查录音权限 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "请先获取录音权限", Toast.LENGTH_SHORT).show(); return; } // 准备录制视频,并启用音频录制 PendingRecording pendingRecording = videoCapture.getOutput() .prepareRecording(VideoRecordActivity.this, options) .withAudioEnabled(); // 如果需要音频录制,调用该方法 // 启动录制,并且设定录制回调 //回调中的videoRecordEvent会有下面几种状态: //VideoRecordEvent.Start:录制开始。 //VideoRecordEvent.Pause:录制暂停。 //VideoRecordEvent.Resume:录制恢复。 //VideoRecordEvent.Finalize:录制完成(停止或失败)。 //VideoRecordEvent.Status:录制状态更新(持续获取统计信息)。 recording = pendingRecording.start(ContextCompat.getMainExecutor(this), videoRecordEvent -> { Log.d(TAG, "Recording videoRecordEvent " + videoRecordEvent); Log.d(TAG, "Recording videoRecordEvent getRecordingStats" + videoRecordEvent.getRecordingStats()); if (videoRecordEvent instanceof VideoRecordEvent.Start) { Log.d(TAG, "Recording started"); isRecording = true; refreshUI(videoRecordEvent); } else if (videoRecordEvent instanceof VideoRecordEvent.Finalize) { Log.d(TAG, "Recording finalized"); Toast.makeText(this, "已保存", Toast.LENGTH_SHORT).show(); isRecording = false; refreshUI(videoRecordEvent); } }); } private void stopRecording() { if (recording != null && isRecording) { recording.stop(); // 停止录制 recording = null; } } //更新UI private void refreshUI(VideoRecordEvent videoRecordEvent) { if (videoRecordEvent instanceof VideoRecordEvent.Start) { //开始录制,把相关的ui换成录制的效果 ivRecord.setImageResource(R.mipmap.icon_stop_record); } else if (videoRecordEvent instanceof VideoRecordEvent.Finalize) { //结束录制 ivRecord.setImageResource(R.mipmap.icon_record); }else{ //这里可以自行扩展其他的状态 } } @Override protected void onDestroy() { super.onDestroy(); cameraExecutor.shutdown(); } }

相关文章:

安卓开发相机功能

相机功能 安卓中的相机调用功能也经历了很多的方案升级&#xff0c;目前可选的官方方案是CameraX、Camera2、Camera&#xff08;废弃&#xff09;&#xff0c;还有一些第三方免费或者是付费的相机库。对于大多数开发者&#xff0c;建议使用 CameraX。 CameraX CameraX 是 An…...

机器学习:监督学习、无监督学习和强化学习

机器学习&#xff08;Machine Learning, ML&#xff09;是人工智能&#xff08;AI&#xff09;的一个分支&#xff0c;它使计算机能够从数据中学习&#xff0c;并在没有明确编程的情况下执行任务。机器学习的核心思想是使用算法分析数据&#xff0c;识别模式&#xff0c;并做出…...

基于vue3和flask开发的前后端管理系统(一):项目启动准备

准备工作 我们需要准备以下工具 vue3&#xff1a;构建前端 tailwind css&#xff1a;样式库vite&#xff1a;快速构建vue项目pinia &#xff1a;vue3 的事件管理器 flask&#xff1a;后端代码Mysql&#xff1a;数据库 heidisql&#xff1a;数据库图形化界面 vscode&#xff1…...

一、MySQL备份恢复

一、MySQL备份恢复 1.1 MySQL日志管理 数据库中数据丢失或被破坏可能原因 误删除数据库 数据库工作时&#xff0c;意外断电或程序意外终止 由于病毒造成的数据库损坏或丢失 文件系统损坏后&#xff0c;系统进行自检操作 升级数据库时&#xff0c;命令语句不严格 设备故…...

DeepSeek崛起:如何在云端快速部署你的专属AI助手

在2025年春节的科技盛宴上&#xff0c;DeepSeek因其在AI领域的卓越表现成为焦点&#xff0c;其开源的推理模型DeepSeek-R1擅长处理多种复杂任务&#xff0c;支持多语言处理&#xff0c;并通过搜索引擎获取实时信息。DeepSeek因其先进的自然语言处理技术、广泛的知识库和高性价比…...

SQLite Alter 命令详解

SQLite Alter 命令详解 SQLite 是一种轻量级的数据库&#xff0c;广泛用于各种嵌入式系统、移动应用和小型项目。SQLite 的ALTER TABLE命令用于修改已存在的表结构&#xff0c;包括添加、删除或修改列&#xff0c;以及重命名表等操作。本文将详细解析SQLite的ALTER TABLE命令&…...

2025 聚合易支付完整版PHP网站源码

源码介绍 2025 聚合易支付完整版PHP网站源码 PHP版本&#xff1a;PHP74 源码上传服务器&#xff0c;解压访问域名即可安装 安装完成后一定要设置伪静态 源码里面nginx.txt 就是伪静态 然后复制粘贴到伪静态里面保存即可 部分截图 源码获取 2025 聚合易支付完整版PHP网站源码…...

Android开发Android调web的方法

Android开发Android调web的方法 一般都是web调Android&#xff0c;很少Android调web方法。 我用的是AgentWeb。它内核也是webview。 直接上代码&#xff1a; mAgentWeb.getJsAccessEntrace().quickCallJs("adLookSuccessAndroid",event.getType());它的意思是&am…...

FastGPT 源码:基于 LLM 实现 Rerank (含Prompt)

文章目录 基于 LLM 实现 Rerank函数定义预期输出实现说明使用建议完整 Prompt 基于 LLM 实现 Rerank 下边通过设计 Prompt 让 LLM 实现重排序的功能。 函数定义 class LLMReranker:def __init__(self, llm_client):self.llm llm_clientdef rerank(self, query: str, docume…...

字节跳动发布 Trae AI IDE!支持 DeepSeek R1 V3,AI 编程新时代来了!

3 月 3 日&#xff0c;字节跳动重磅发布国内首款 AI 原生集成开发环境&#xff08;AI IDE&#xff09;——Trae 国内版&#xff01; Trae 不只是一个传统的 IDE&#xff0c;它深度融合 AI&#xff0c;搭载 doubao-1.5-pro 大模型&#xff0c;同时支持DeepSeek R1 & V3&…...

windows下安装Open Web UI

windows下安装openwebui有三种方式,docker,pythonnode.js,整合包. 这里我选择的是第二种,非docker. 非Docker方式安装 1. 安装Python&#xff1a; 下载并安装Python 3.11&#xff0c;建议安装路径中不要包含中文字符&#xff0c;并勾选“Add python 3.11 to Path”选项。 安…...

论文阅读 EEG-Inception

EEG-Inception: A Novel Deep Convolutional Neural Network for Assistive ERP-Based Brain-Computer Interfaces EEG-Inception是第一个集成Inception模块进行ERP检测的模型&#xff0c;它有效地结合了轻型架构中的其他结构&#xff0c;提高了我们方法的性能。 本研究的主要目…...

基于opencv消除图片马赛克

以下是一个基于Python的图片马赛克消除函数实现&#xff0c;结合了图像处理和深度学习方法。由于马赛克消除涉及复杂的图像重建任务&#xff0c;建议根据实际需求选择合适的方法&#xff1a; import cv2 import numpy as np from PIL import Imagedef remove_mosaic(image_pat…...

计算机毕业设计SpringBoot+Vue.js陕西民俗网(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

【算法方法总结·三】滑动窗口的一些技巧和注意事项

【算法方法总结三】滑动窗口的一些技巧和注意事项 【算法方法总结一】二分法的一些技巧和注意事项【算法方法总结二】双指针的一些技巧和注意事项【算法方法总结三】滑动窗口的一些技巧和注意事项 【滑动窗口】 数组的和 随着 右边指针 移动一定是 非递减 的&#xff0c;就是 …...

IO的概念和标准IO函数

作业&#xff1a; 1.使用标准IO函数&#xff0c;实现文件的拷贝 #include <stdio.h>int main(int argc, char *argv[]) {// 检查是否提供了源文件和目标文件if (argc ! 3) {printf("Usage: %s <source_file> <destination_file>\n", argv[0]);re…...

tauri2+typescript+vue+vite+leaflet等的简单联合使用(一)

项目目标 主要的目的是学习tauri。 流程 1、搭建项目 2、简单的在项目使用leaflet 3、打包 准备项目 环境准备 废话不多说&#xff0c;直接开始 需要有准备能运行Rust的环境和Node&#xff0c;对于Rust可以参考下面这位大佬的文章&#xff0c;Node不必细说。 Rust 和…...

【流程图】在 .NET (WPF 或 WinForms) 中实现流程图中的连线算法

在 .NET (WPF 或 WinForms) 中实现流程图中的连线算法&#xff0c;通常涉及 图形绘制 和 路径计算。常见的连线方式包括 直线、折线 和 贝塞尔曲线。以下是几种方法的介绍和示例代码。 1. 直线连接&#xff08;最简单&#xff09; 适用场景&#xff1a; 两个节点之间没有障碍…...

IDEA集成DeepSeek,通过离线安装解决无法安装Proxy AI插件问题

文章目录 引言一、安装Proxy AI1.1 在线安装Proxy AI1.2 离线安装Proxy AI 二、Proxy AI中配置DeepSeek2.1 配置本地部署的DeepSeek&#xff08;Ollama方式&#xff09;2.2 通过第三方服务商提供的API进行配置 三、效果测试 引言 许多开发者尝试通过安装Proxy AI等插件将AI能力…...

【流行病学】Melodi-Presto因果关联工具

title: “[流行病学] Melodi Presto因果关联工具” date: 2022-12-08 lastmod: 2022-12-08 draft: false tags: [“流行病学”,“因果关联工具”] toc: true autoCollapseToc: true 阅读介绍 Melodi-Presto: A fast and agile tool to explore semantic triples derived from …...

华为BGP路由实战:从原理到策略调优的深度解析

1. 华为BGP路由技术入门指南 第一次接触华为BGP路由配置时&#xff0c;我被那些专业术语搞得晕头转向。经过多次实战后才发现&#xff0c;BGP就像互联网世界的邮局系统&#xff0c;负责在不同自治系统&#xff08;AS&#xff09;之间传递路由信息。华为设备的BGP实现特别适合企…...

智慧零售技术架构解析:从智能终端到边缘计算,如何重塑购物体验

1. 智慧零售的“科技感”从何而来&#xff1f;最近&#xff0c;一段关于智能购物车的视频火了。视频里&#xff0c;消费者推着一辆看似普通&#xff0c;实则“暗藏玄机”的购物车在超市里穿梭&#xff0c;无需排队&#xff0c;扫码即走&#xff0c;最后在出口处轻松完成支付。这…...

别再只用差速轮了!手把手教你为Navigation2仿真打造专属阿克曼底盘模型(附完整URDF/SDF文件)

从差速轮到阿克曼&#xff1a;打造高仿真Navigation2底盘模型的完整指南 在机器人仿真领域&#xff0c;差速轮底盘因其简单可靠而广受欢迎&#xff0c;但真实世界的车辆大多采用阿克曼转向机制。本文将带您深入理解两种模型的本质差异&#xff0c;并手把手指导如何从零构建或改…...

Configor 自动重载功能深度解析:实现配置热更新的终极指南

Configor 自动重载功能深度解析&#xff1a;实现配置热更新的终极指南 【免费下载链接】configor Golang Configuration tool that support YAML, JSON, TOML, Shell Environment 项目地址: https://gitcode.com/gh_mirrors/co/configor Configor 是 Golang 生态系统中一…...

PostgreSQL列式存储革命:cstore_fdw完整指南与10个性能优化技巧

PostgreSQL列式存储革命&#xff1a;cstore_fdw完整指南与10个性能优化技巧 【免费下载链接】cstore_fdw Columnar storage extension for Postgres built as a foreign data wrapper. Check out https://github.com/citusdata/citus for a modernized columnar storage implem…...

NIC-400周期模型构建失败分析与解决方案

1. 问题概述&#xff1a;NIC-400周期模型构建失败场景分析最近在Arm IP Exchange平台上构建NIC-400周期模型时&#xff0c;不少工程师遇到了构建失败的问题。作为Arm CoreLink系列网络互连控制器的重要验证工具&#xff0c;周期模型的正确构建直接影响后续系统级验证的效率。根…...

别再只会if-else了!用状态机思路重构你的STM32寻迹小车代码(附工程源码)

从if-else到状态机&#xff1a;重构STM32寻迹小车的工程化实践 当三个红外传感器同时检测到黑色轨迹时&#xff0c;你的小车应该左转还是右转&#xff1f;当传感器短暂丢失信号时&#xff0c;是紧急刹车还是保持原有动作&#xff1f;这些问题在初学者用if-else堆砌的代码中往往…...

告别通用OCR:如何用PaddleOCR针对银行卡场景做定制化检测模型优化?

告别通用OCR&#xff1a;如何用PaddleOCR针对银行卡场景做定制化检测模型优化&#xff1f; 银行卡识别一直是金融科技领域的高频需求&#xff0c;但通用OCR模型在应对银行卡这类特殊场景时往往力不从心。我曾参与过多个银行的移动端项目&#xff0c;亲眼见证过通用模型在识别卡…...

Agent+可穿戴设备:心率、睡眠、活动数据如何变成有价值的健康建议

可穿戴设备每天都会产生心率、睡眠、步数、活动强度等数据&#xff0c;但开发者真正要解决的不是“采集更多指标”&#xff0c;而是把这些指标转成可解释、可追踪、可配置的健康提示。本文从工程角度搭建一个简化版 Agent 服务&#xff0c;演示如何完成数据接入、趋势计算、规则…...

研究助理/项目经理/内容编辑:Hermes Agent 3 类人格模板的 SOUL.md 配置要点

1. 三类人格不是“角色扮演”,而是上下文锚点的工程化切片 大多数人第一次看到 Hermes Agent 的 SOUL.md 配置时,会下意识把它当成一个“AI人设说明书”:研究助理要严谨、项目经理要干练、内容编辑要文雅。这种理解在小规模单次交互中勉强能用,但一旦进入真实研发流程——…...