Halo 正式开源: 使用可穿戴设备进行开源健康追踪

9df49fdb73087b8545d1140f278dc16f.jpeg

在飞速发展的可穿戴技术领域,我们正处于一个十字路口——市场上充斥着各式时尚、功能丰富的设备,声称能够彻底改变我们对健康和健身的方式。

然而,在这些光鲜的外观和营销宣传背后,隐藏着一个令人担忧的现实:大多数这些设备是封闭系统,其内部运行被专有代码和封闭硬件所掩盖。作为消费者,我们对这些设备如何收集、处理及可能共享我们的健康数据一无所知。

这时,Halo 出现了,它是一种旨在让健康追踪更加普惠化的开源替代方案。通过这系列文章,我们将引导你从基础入手,构建并使用完全透明、可定制的可穿戴设备。

需要说明的是,Halo 的目标并不是在外观或功能完整性上与消费级可穿戴设备竞争。相反,它提供了一种独特的、动手实践的方式来理解健康追踪设备背后的技术。

我们将使用 Swift 5 来构建对应的 iOS 界面,Python >= 3.10。由于此项目的代码完全开源,你可以随时提交 PR 拉取请求,或者 Fork 分叉项目以探索全新的方向。

  • 开源https://github.com/cyrilzakka/Halo-iOS

你将需要:

  • 获取COLMI R02实体设备,价格在撰写时为 11 到 30 美金左右。https://www.aliexpress.us/item/3256806445134241.html?gatewayAdapt=glo2usa4itemAdapt

  • 一个安装了 Xcode 16 的开发环境,以及可选的 Apple 开发者计划会员资格。

  • Python >= 3.10,并安装了 pandasnumpytorch 当然还有 transformers

致谢

此项目基于Python 仓库的代码及我的学习成果构建。

  • Python 仓库https://tahnok.github.io/colmi_r02_client/

免责声明

作为一名医生,我有法律义务提醒你:你即将阅读的内容并不是医学建议。现在,让我们开始让一些可穿戴设备发出蜂鸣声吧!

配对戒指

在进入代码之前,让我们先了解蓝牙低能耗 (BLE) 的关键规格。BLE 基于一个简单的客户端-服务器模型,使用三个核心概念:中央设备 (Centrals)服务 (Services) 和 **特征 (Characteristics)**。以下是它们的具体介绍:

  • 中央设备 (例如你的 iPhone) 负责启动和管理与外设 (例如我们的 COLMI R02 戒指) 的连接。戒指通过广播自身信息等待手机连接,每次仅支持一台手机连接。

  • 服务 是戒指上相关功能的集合,例如心率监测服务或电池状态服务。每个服务都有一个唯一标识符 (UUID) ,客户端通过它来找到对应服务。

  • 特征 是每个服务中的具体数据点或控制机制。例如,它们可能是只读 (获取传感器数据) 、只写 (发送命令) 或两者兼有。有些特征还能在其值发生变化时自动通知手机,这对于实时健康监测尤为重要。

当手机连接到戒指时,会定位所需的服务,并与特定特征交互以发送命令或接收数据。这种结构化的方法不仅确保了通信效率,还能延长电池使用时间。了解了这些基础知识后,让我们开始构建吧!

设置 Xcode 项目

创建一个名为 Halo 的新项目,目标平台为 iOS。组织标识符建议使用反向域名格式 (如 com.example) 。本项目中,我们使用 com.FirstNameLastName

接下来,为应用启用必要的功能。在 Xcode 中,打开 Signing & Capabilities 选项卡,启用以下 后台模式 (Background Modes),以确保应用在后台运行时能够保持与戒指的连接并处理数据。

然后,我们将使用 Apple 提供的最新框架AccessorySetupKit,用于将蓝牙和 Wi-Fi 配件连接到 iOS 应用。此框架自 iOS 18 推出,替代了传统的广泛蓝牙权限请求方式,专注于为用户明确批准的特定设备提供访问权限。

  • AccessorySetupKithttps://developer.apple.com/documentation/accessorysetupkit/

当用户尝试将 COLMI R02 戒指连接到应用时,AccessorySetupKit 会显示一个系统界面,仅列出兼容的附近设备。用户选择设备后,应用即可与戒指通信,而无需请求完整的蓝牙权限。这大大提升了用户隐私,同时简化了设备连接的管理流程。

打开 Info.plist 文件 (可以在左侧边栏中找到,或通过 Project Navigator (⌘1) > Your Target > Info 定位) 。添加以下键值条目以支持与 COLMI R02 戒指的配对:

  • 添加 NSAccessorySetupKitSupports,类型为 Array,并将 Bluetooth 作为第一个项目。

  • 添加 NSAccessorySetupBluetoothServices,类型为 Array,并将以下 UUID 作为 String 项:

    • 6E40FFF0-B5A3-F393-E0A9-E50E24DCCA9E

    • 0000180A-0000-1000-8000-00805F9B34FB

至此,初步配置完成!🤗

303b2be869837b0a57d00f54639c1914.png

Ring Session Manager 类

接下来,我们将创建一个 RingSessionManager 类,用于管理所有与戒指的通信。此类的主要职责包括:

  • 扫描附近的戒指

  • 连接到戒指

  • 发现服务和特征

  • 实现数据读写操作

第一步:创建 RingSessionManager

首先创建一个新的 Swift 文件 (⌘N) ,命名为 RingSessionManager.swift。以下是类的定义以及需要实现的关键属性:

@Observable
class RingSessionManager: NSObject {// 追踪连接状态var peripheralConnected = falsevar pickerDismissed = true// 存储当前连接的戒指var currentRing: ASAccessory?private var session = ASAccessorySession()// 核心蓝牙对象private var manager: CBCentralManager?private var peripheral: CBPeripheral?
}
第二步:发现戒指

戒指通过特定的蓝牙服务 UUID 进行广播。为了找到它,我们需要创建一个 ASDiscoveryDescriptor 对象,指定其蓝牙服务的 UUID。以下代码完成了这一功能:

private static let ring: ASPickerDisplayItem = {let descriptor = ASDiscoveryDescriptor()descriptor.bluetoothServiceUUID = CBUUID(string: "6E40FFF0-B5A3-F393-E0A9-E50E24DCCA9E")return ASPickerDisplayItem(name: "COLMI R02 Ring",productImage: UIImage(named: "colmi")!,descriptor: descriptor)
}()

确保将戒指图片添加到项目资源目录中,或者用合适的占位符替换 UIImage(named: "colmi")!

第三步:显示戒指选择器

为了让用户选择戒指,我们调用系统内置的设备选择器界面:

func presentPicker() {session.showPicker(for: [Self.ring]) { error inif let error {print("Failed to show picker: \(error.localizedDescription)")}}
}
第四步:处理戒指选择

当用户从选择器中选定设备后,应用需要处理连接和管理逻辑。以下代码实现了事件处理:

private func handleSessionEvent(event: ASAccessoryEvent) {switch event.eventType {case .accessoryAdded:guard let ring = event.accessory else { return }saveRing(ring: ring)case .activated:// 重新连接已配对戒指guard let ring = session.accessories.first else { return }saveRing(ring: ring)case .accessoryRemoved:currentRing = nilmanager = nil}
}
第五步:建立连接

完成选择戒指后,我们需要与其建立蓝牙连接:

func connect() {guard let manager, manager.state == .poweredOn, let peripheral else { return }let options: [String: Any] = [CBConnectPeripheralOptionNotifyOnConnectionKey: true,CBConnectPeripheralOptionNotifyOnDisconnectionKey: true,CBConnectPeripheralOptionStartDelayKey: 1]manager.connect(peripheral, options: options)
}
第六步:理解委托方法

RingSessionManager 中,我们实现了两个关键的委托协议,用于管理蓝牙通信过程。

中央管理器委托 (CBCentralManagerDelegate)此委托主要处理蓝牙连接的整体状态。

func centralManagerDidUpdateState(_ central: CBCentralManager) {print("Central manager state: \(central.state)")switch central.state {case .poweredOn:if let peripheralUUID = currentRing?.bluetoothIdentifier {if let knownPeripheral = central.retrievePeripherals(withIdentifiers: [peripheralUUID]).first {print("Found previously connected peripheral")peripheral = knownPeripheralperipheral?.delegate = selfconnect()} else {print("Known peripheral not found, starting scan")}}default:peripheral = nil}
}

当蓝牙开启时,程序会检查是否有已连接的戒指,并尝试重新连接。
成功连接后:

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {print("DEBUG: Connected to peripheral: \(peripheral)")peripheral.delegate = selfprint("DEBUG: Discovering services...")peripheral.discoverServices([CBUUID(string: Self.ringServiceUUID)])peripheralConnected = true
}

断开连接时:

func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: (any Error)?) {print("Disconnected from peripheral: \(peripheral)")peripheralConnected = falsecharacteristicsDiscovered = false
}

外设委托 (CBPeripheralDelegate)

此委托主要处理与戒指的具体通信。
首先发现戒指的服务:

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: (any Error)?) {print("DEBUG: Services discovery callback, error: \(String(describing: error))")guard error == nil, let services = peripheral.services else {print("DEBUG: No services found or error occurred")return}print("DEBUG: Found \(services.count) services")for service in services {if service.uuid == CBUUID(string: Self.ringServiceUUID) {print("DEBUG: Found ring service, discovering characteristics...")peripheral.discoverCharacteristics([CBUUID(string: Self.uartRxCharacteristicUUID),CBUUID(string: Self.uartTxCharacteristicUUID)], for: service)}}
}

发现特征后:

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {print("DEBUG: Characteristics discovery callback, error: \(String(describing: error))")guard error == nil, let characteristics = service.characteristics else {print("DEBUG: No characteristics found or error occurred")return}print("DEBUG: Found \(characteristics.count) characteristics")for characteristic in characteristics {switch characteristic.uuid {case CBUUID(string: Self.uartRxCharacteristicUUID):print("DEBUG: Found UART RX characteristic")self.uartRxCharacteristic = characteristiccase CBUUID(string: Self.uartTxCharacteristicUUID):print("DEBUG: Found UART TX characteristic")self.uartTxCharacteristic = characteristicperipheral.setNotifyValue(true, for: characteristic)default:print("DEBUG: Found other characteristic: \(characteristic.uuid)")}}characteristicsDiscovered = true
}

接收数据时:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {if characteristic.uuid == CBUUID(string: Self.uartTxCharacteristicUUID) {if let value = characteristic.value {print("Received value: \(value)")}}
}

发送命令后:

func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {if let error = error {print("Write to characteristic failed: \(error.localizedDescription)")} else {print("Write to characteristic successful")}
}
完整代码

完整的 RingSessionManager 类代码如下:

import Foundation
import AccessorySetupKit
import CoreBluetooth
import SwiftUI@Observable
class RingSessionManager: NSObject {var peripheralConnected = falsevar pickerDismissed = truevar currentRing: ASAccessory?private var session = ASAccessorySession()private var manager: CBCentralManager?private var peripheral: CBPeripheral?private var uartRxCharacteristic: CBCharacteristic?private var uartTxCharacteristic: CBCharacteristic?private static let ringServiceUUID = "6E40FFF0-B5A3-F393-E0A9-E50E24DCCA9E"private static let uartRxCharacteristicUUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"private static let uartTxCharacteristicUUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"private static let deviceInfoServiceUUID = "0000180A-0000-1000-8000-00805F9B34FB"private static let deviceHardwareUUID = "00002A27-0000-1000-8000-00805F9B34FB"private static let deviceFirmwareUUID = "00002A26-0000-1000-8000-00805F9B34FB"private static let ring: ASPickerDisplayItem = {let descriptor = ASDiscoveryDescriptor()descriptor.bluetoothServiceUUID = CBUUID(string: ringServiceUUID)return ASPickerDisplayItem(name: "COLMI R02 Ring",productImage: UIImage(named: "colmi")!,descriptor: descriptor)}()private var characteristicsDiscovered = falseoverride init() {super.init()self.session.activate(on: DispatchQueue.main, eventHandler: handleSessionEvent(event:))}// MARK: - RingSessionManager actionsfunc presentPicker() {session.showPicker(for: [Self.ring]) { error inif let error {print("Failed to show picker due to: \(error.localizedDescription)")}}}func removeRing() {guard let currentRing else { return }if peripheralConnected {disconnect()}session.removeAccessory(currentRing) { _ inself.currentRing = nilself.manager = nil}}func connect() {guardlet manager, manager.state == .poweredOn,let peripheralelse {return}let options: [String: Any] = [CBConnectPeripheralOptionNotifyOnConnectionKey: true,CBConnectPeripheralOptionNotifyOnDisconnectionKey: true,CBConnectPeripheralOptionStartDelayKey: 1]manager.connect(peripheral, options: options)}func disconnect() {guard let peripheral, let manager else { return }manager.cancelPeripheralConnection(peripheral)}// MARK: - ASAccessorySession functionsprivate func saveRing(ring: ASAccessory) {currentRing = ringif manager == nil {manager = CBCentralManager(delegate: self, queue: nil)}}private func handleSessionEvent(event: ASAccessoryEvent) {switch event.eventType {case .accessoryAdded, .accessoryChanged:guard let ring = event.accessory else { return }saveRing(ring: ring)case .activated:guard let ring = session.accessories.first else { return }saveRing(ring: ring)case .accessoryRemoved:self.currentRing = nilself.manager = nilcase .pickerDidPresent:pickerDismissed = falsecase .pickerDidDismiss:pickerDismissed = truedefault:print("Received event type \(event.eventType)")}}
}// MARK: - CBCentralManagerDelegate
extension RingSessionManager: CBCentralManagerDelegate {func centralManagerDidUpdateState(_ central: CBCentralManager) {print("Central manager state: \(central.state)")switch central.state {case .poweredOn:if let peripheralUUID = currentRing?.bluetoothIdentifier {if let knownPeripheral = central.retrievePeripherals(withIdentifiers: [peripheralUUID]).first {print("Found previously connected peripheral")peripheral = knownPeripheralperipheral?.delegate = selfconnect()} else {print("Known peripheral not found, starting scan")}}default:peripheral = nil}}func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {print("DEBUG: Connected to peripheral: \(peripheral)")peripheral.delegate = selfprint("DEBUG: Discovering services...")peripheral.discoverServices([CBUUID(string: Self.ringServiceUUID)])peripheralConnected = true}func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: (any Error)?) {print("Disconnected from peripheral: \(peripheral)")peripheralConnected = falsecharacteristicsDiscovered = false}func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: (any Error)?) {print("Failed to connect to peripheral: \(peripheral), error: \(error.debugDescription)")}
}// MARK: - CBPeripheralDelegate
extension RingSessionManager: CBPeripheralDelegate {func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: (any Error)?) {print("DEBUG: Services discovery callback, error: \(String(describing: error))")guard error == nil, let services = peripheral.services else {print("DEBUG: No services found or error occurred")return}print("DEBUG: Found \(services.count) services")for service in services {if service.uuid == CBUUID(string: Self.ringServiceUUID) {print("DEBUG: Found ring service, discovering characteristics...")peripheral.discoverCharacteristics([CBUUID(string: Self.uartRxCharacteristicUUID),CBUUID(string: Self.uartTxCharacteristicUUID)], for: service)}}}func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {print("DEBUG: Characteristics discovery callback, error: \(String(describing: error))")guard error == nil, let characteristics = service.characteristics else {print("DEBUG: No characteristics found or error occurred")return}print("DEBUG: Found \(characteristics.count) characteristics")for characteristic in characteristics {switch characteristic.uuid {case CBUUID(string: Self.uartRxCharacteristicUUID):print("DEBUG: Found UART RX characteristic")self.uartRxCharacteristic = characteristiccase CBUUID(string: Self.uartTxCharacteristicUUID):print("DEBUG: Found UART TX characteristic")self.uartTxCharacteristic = characteristicperipheral.setNotifyValue(true, for: characteristic)default:print("DEBUG: Found other characteristic: \(characteristic.uuid)")}}characteristicsDiscovered = true}func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {if characteristic.uuid == CBUUID(string: Self.uartTxCharacteristicUUID) {if let value = characteristic.value {print("Received value: \(value)")}}}func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {if let error = error {print("Write to characteristic failed: \(error.localizedDescription)")} else {print("Write to characteristic successful")}}
}

最后一步:将其应用到我们的应用程序中

ContentView.swift 中粘贴以下代码,作为主界面的一部分:

import SwiftUI
import AccessorySetupKitstruct ContentView: View {@State var ringSessionManager = RingSessionManager()var body: some View {List {Section("MY DEVICE", content: {if ringSessionManager.pickerDismissed, let currentRing = ringSessionManager.currentRing {makeRingView(ring: currentRing)} else {Button {ringSessionManager.presentPicker()} label: {Text("Add Ring").frame(maxWidth: .infinity).font(Font.headline.weight(.semibold))}}})}.listStyle(.insetGrouped)}@ViewBuilderprivate func makeRingView(ring: ASAccessory) -> some View {HStack {Image("colmi").resizable().aspectRatio(contentMode: .fit).frame(height: 70)VStack(alignment: .leading) {Text(ring.displayName).font(Font.headline.weight(.semibold))}}}
}#Preview {ContentView()
}

如果一切配置正确,你现在可以构建并运行应用。当点击“Add Ring”按钮时,将弹出一个界面,显示附近的兼容设备 (包括 COLMI R02 戒指) 。选择设备后,应用即可完成连接。🎉

db1da38b8db10b60be8cd8e6d99734b6.png
连接演示

在后续的文章中,我们将进一步探索如何与戒指交互,包括读取电池电量、获取传感器数据 (如 PPG 和加速度计) ,并基于这些数据开发实时心率监测、活动追踪及睡眠检测功能。敬请期待!

英文原文:https://hf.co/blog/cyrilzakka/halo-introduction

原文作者: Cyril, ML Researcher, Health AI Lead @ Hugging Face

译者: Lu Cheng, Hugging Face Fellow

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

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

相关文章

Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在数字化时代,…

嵌入式中利用QT实现服务器与客户端方法

大家好,今天主要给大家分享一下,如何使用QT中TCP协议进行传输控制,它是一种面向连接的,可靠的基于字节流的传输层控制协议。 第一:Linux中网络通信简介 TCP通信必须建立TCP连接,通信端分为客户端和服务端。服务端通过监听某个端口来监听是否有客户端连接进来,如果有连接…

网络安全,文明上网(6)网安相关法律

列举 1. 《中华人民共和国网络安全法》: - 这是中国网络安全的基本法律,于2017年6月1日开始实施。该法律明确了网络运营者的安全保护义务,包括采取数据分类、重要数据备份和加密等措施。 2. 《中华人民共和国数据安全法》: …

Vscode写markdown快速插入python代码

如图当我按下快捷键CRTLSHIFTK 自动出现python代码片段 配置方法shortcuts’ 打开这个json文件 输入 {"key": "ctrlshiftk","command": "editor.action.insertSnippet","when": "editorTextFocus","args&…

【前端】第12节:Vue3新特性

引入 说起 vue3 的新特性,就会不由自主想到 vue3 和 vue2 之间的差异,例如:双向绑定、根节点数量、生命周期、this 等等,详细可以见这篇文章(参考)—— vue2和vue3的差异整理(轻松过度到vue3&a…

Linux 进程概念与进程状态

目录 1. 冯诺依曼体系结构2. 操作系统(Operator System)2.1 概念2.2 设计OS的目的2.3 系统调用和库函数概念 3. 进程概念3.1 描述进程 - PCB3.2 task_struct3.3 查看进程3.4 通过系统调用获取进程标识符PID, PPID3.5 通过系统调用创建fork 4.…

滑动窗口篇——如行云流水般的高效解法与智能之道(1)

前言: 上篇我们介绍了双指针算法,并结合具体题目进行了详细的运用讲解。本篇我们将会了解滑动窗口。滑动窗口是一种常用的算法技巧,主要用于处理子数组、子串等具有“窗口”特性的题目。柳暗花明,乃巧解复杂问题的高效之道。 一. …

网络安全-企业环境渗透2-wordpress任意文件读FFmpeg任意文件读

一、 实验名称 企业环境渗透2 二、 实验目的 【实验描述】 操作机的操作系统是kali 进入系统后默认是命令行界面 输入startx命令即可打开图形界面。 所有需要用到的信息和工具都放在了/home/Hack 目录下。 本实验的任务是通过外网的两个主机通过代理渗透到内网的两个主机。…

DB-GPT V0.6.2 版本更新:牵手libro社区、GraphRAG图谱构建能力增强等

DB-GPT V0.6.2版本现已上线,快速预览新特性: 新特性 1、DB-GPT 社区和 libro 社区共同发布 AWEL Notebook 功能 libro:灵活定制、轻松集成的 Notebook 产品方案。 社区地址:https://github.com/difizen/libro 使用教程&#xf…

GPT1.0 和 GPT2.0 的联系与区别

随着自然语言处理技术的飞速发展,OpenAI 提出的 GPT 系列模型成为了生成式预训练模型的代表。作为 GPT 系列的两代代表,GPT-1 和 GPT-2 虽然在架构上有着继承关系,但在设计理念和性能上有显著的改进。本文将从模型架构、参数规模、训练数据和…

本地部署与外部部署有何不同?

什么是本地部署? 本地部署(通常缩写为“on-prem”)是指在公司自己的设施或数据中心内托管的软件和基础设施。与基于云的解决方案不同,本地部署系统让企业对其数据、硬件和软件配置拥有完全的控制权。这种设置非常适合那些优先考虑…

游戏引擎学习第20天

解释 off-by-one 错误 从演讲者的视角:对代码问题的剖析与修复过程 问题的起因 演讲者提到,他可能无意中在代码中造成了一个错误,这与“调试时间标记索引”有关。他发现了一个逻辑问题,即在检查数组边界时,使用了“调试…

Android-如何实现Apng动画播放

01 Apng是什么 Apng(Animated Portable Network Graphics)顾名思义是基于 PNG 格式扩展的一种动画格式,增加了对动画图像的支持,同时加入了 24 位图像和8位 Alpha 透明度的支持,并且向下兼容 PNG。 Google封面图 02 A…

Linux下Intel编译器oneAPI安装和链接MKL库编译

参考: https://blog.csdn.net/qq_44263574/article/details/123582481 官网下载: https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit-download.html?packagesoneapi-toolkit&oneapi-toolkit-oslinux&oneapi-linoffline 填写邮件和国家,…

【Python系列】浅析 Python 中的字典更新与应用场景

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Matlab科研绘图:自定义内置多款配色函数

在Matlab科研绘图中,自定义和使用内置的多款配色函数可以极大地增强图表的视觉效果和数据的可读性。本文将介绍配色函数,共计带来6套配色体系,而且后续可以根据需要修改,帮助大家自定义和使用配色函数。 1.配色函数 可以根据个…

网络安全的学习方向和路线是怎么样的?

最近有同学问我,网络安全的学习路线是怎么样的? 废话不多说,先上一张图镇楼,看看网络安全有哪些方向,它们之间有什么关系和区别,各自需要学习哪些东西。 在这个圈子技术门类中,工作岗位主要有以…

JAVA八股与代码实践----JDK代理和CGLIB代理的区别

当spring发现该代理的类实现了接口会使用JDK代理,如果该类没有实现接口,会使用CGLIB代理 如果在springboot中要强制使用CGLIB代理,需要加上 EnableAspectJAutoProxy(proxyTargetClass true) // 强制使用 CGLIB SpringBootApplication Ena…

环境背景文本到语音转换

目录 概述演示效果核心逻辑使用方式 概述 本文所涉及的所有资源的获取方式:https://www.aspiringcode.com/content?id100000000027&uid2f1061526e3a4548ab2e111ad079ea8c 论文标题: 本文提出了 VoiceLDM,这是一种旨在生成准确遵循两种…

mac安装Pytest、Allure、brew

安装环境 安装pytest 命令 pip3 install pytest 安装allure 命令:brew install allure 好吧 那我们在安装allure之前 我们先安装brew 安装brew 去了官网复制了命令 还是无法下载 如果你们也和我一样可以用这个方法哦 使用国内的代码仓库来执行brew的安装脚本…