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 = false
    var 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 in
        if 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 = nil
        manager = 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 = knownPeripheral
                peripheral?.delegate = self
                connect()
            } 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 = self
    print("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 = false
    characteristicsDiscovered = 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 = characteristic
        case CBUUID(string: Self.uartTxCharacteristicUUID):
            print("DEBUG: Found UART TX characteristic")
            self.uartTxCharacteristic = characteristic
            peripheral.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 = false
    var pickerDismissed = true
    
    var 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 = false
    
    override init() {
        super.init()
        self.session.activate(on: DispatchQueue.main, eventHandler: handleSessionEvent(event:))
    }
    
    // MARK: - RingSessionManager actions
    func presentPicker() {
        session.showPicker(for: [Self.ring]) { error in
            if let error {
                print("Failed to show picker due to: \(error.localizedDescription)")
            }
        }
    }
    
    func removeRing() {
        guard let currentRing else { return }
        
        if peripheralConnected {
            disconnect()
        }
        
        session.removeAccessory(currentRing) { _ in
            self.currentRing = nil
            self.manager = 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)
    }
    
    func disconnect() {
        guard let peripheral, let manager else { return }
        manager.cancelPeripheralConnection(peripheral)
    }
    
    // MARK: - ASAccessorySession functions
    private func saveRing(ring: ASAccessory) {
        currentRing = ring
        
        if 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 = nil
            self.manager = nil
        case .pickerDidPresent:
            pickerDismissed = false
        case .pickerDidDismiss:
            pickerDismissed = true
        default:
            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 = knownPeripheral
                    peripheral?.delegate = self
                    connect()
                } 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 = self
        print("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 = false
        characteristicsDiscovered = 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 = characteristic
            case CBUUID(string: Self.uartTxCharacteristicUUID):
                print("DEBUG: Found UART TX characteristic")
                self.uartTxCharacteristic = characteristic
                peripheral.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 AccessorySetupKit

struct 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)
    }
    
    @ViewBuilder
    private 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙NEXT开发案例:随机数生成

【引言】 本项目是一个简单的随机数生成器应用,用户可以通过设置随机数的范围和个数,并选择是否允许生成重复的随机数,来生成所需的随机数列表。生成的结果可以通过点击“复制”按钮复制到剪贴板。 【环境准备】 • 操作系统:W…

Linux 下的IO模型

一:四种IO模 1.1:阻塞式IO(最简单,最常用,效率最低) 阻塞I/O 模式是最普遍使用的I/O 模式,大部分程序使用的都是阻塞模式的I/O 。 缺省情况下(及系统默认状态)&#xf…

深度可观察性:它是什么,为什么我们需要它?

随着混合云基础设施成为现代企业的支柱,威胁形势的发展速度比以往任何时候都快。 越来越老练的攻击者、隐藏的漏洞和复杂的监管要求使 IT 和安全团队更难确保在这个复杂的环境中提供强大的保护。 2024 年 Gigamon 混合云安全调查显示,安全和 IT 领导者…

【附代码原理】偏正态分布的数据处理方法

文章目录 相关教程基本信息数学定义参数的影响Python 实现安装 scipy生成和绘制偏正态分布的数据 解释应用 右偏(即长尾在右侧)的正态分布1. 对数变换 (Log Transformation)2. 平方根变换 (Square Root Transformation)3. Box-Cox 变换注意事项 左偏&…

【STM32】MPU6050简介

文章目录 MPU6050简介MPU6050关键块带有16位ADC和信号调理的三轴MEMS陀螺仪具有16位ADC和信号调理的三轴MEMS加速度计I2C串行通信接口 MPU6050对应的数据手册:MPU6050 陀螺仪加速度计 链接: https://pan.baidu.com/s/13nwEhGvsfxx0euR2hMHsyw?pwdv2i6 提取码: v2i6…

一篇快速上手 Axios,一个基于 Promise 的网络请求库(涉及原理实现)

Axios 1. 介绍1.1 什么是 Axios?1.2 axios 和 ajax 的区别 2. 安装使用3. Axios 基本使用3.1 Axios 发送请求3.2 其他方式发送请求3.3 响应结构3.4 Request Config3.5 默认配置3.6 创建实例对象发送请求 3.7 拦截器3.8 取消请求 4. 模拟 Axios4.1 axios 对象创建过程…

趋势洞察|AI 能否带动裸金属 K8s 强势崛起?

随着容器技术的不断成熟,不少企业在开展私有化容器平台建设时,首要考虑的问题就是容器的部署环境——是采用虚拟机还是物理机运行容器?在往期“虚拟化 vs. 裸金属*”系列文章中,我们分别对比了容器部署在虚拟化平台和物理机上的架…

Unity-添加世界坐标系辅助线

如果你想在场景中更直观地显示世界坐标系,可以通过编写一个简单的脚本来实现。下面是一个基本的示例脚本,它会在场景中绘制出世界坐标系的三个轴: using UnityEngine;public class WorldAxesIndicator : MonoBehaviour {public float length…

决策树分类算法【sklearn/决策树分裂指标/鸢尾花分类实战】

决策树分类算法 1. 什么是决策树?2. DecisionTreeClassifier的使用(sklearn)2.1 算例介绍2.2 构建决策树并实现可视化 3. 决策树分裂指标3.1 信息熵(ID3)3.2 信息增益3.3 基尼指数(CART) 4. 代码…

5分钟轻松搭建Immich图片管理软件并实现公网远程传输照片

文章目录 前言1.关于Immich2.安装Docker3.本地部署Immich4.Immich体验5.安装cpolar内网穿透6.创建远程链接公网地址7.使用固定公网地址远程访问 前言 本篇文章介绍如何在本地搭建lmmich图片管理软件,并结合cpolar内网穿透实现公网远程访问到局域网内的lmmich&#…

React和Next.js的相关内容

React–前端框架 React 是一个用于构建用户界面的 JAVASCRIPT 库。 React 主要用于构建 UI,很多人认为 React 是 MVC 中的 V(视图)。 React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 …

【LeetCode热题100】队列+宽搜

这篇博客是关于队列宽搜的几道题&#xff0c;主要包括N叉树的层序遍历、二叉树的锯齿形层序遍历、二叉树最大宽度、在每个数行中找最大值。 class Solution { public:vector<vector<int>> levelOrder(Node* root) {vector<vector<int>> ret;if(!root) …

丹摩征文活动|基于丹摩算力的可图(Kolors)的部署与使用

Kolors是一个以生成图像为目标的人工智能系统&#xff0c;可能采用了类似于OpenAI的DALLE、MidJourney等文本生成图像的技术。通过自然语言处理&#xff08;NLP&#xff09;和计算机视觉&#xff08;CV&#xff09;相结合&#xff0c;Kolors能够根据用户提供的文本描述生成符合…

【PTA】【数据库】【SQL命令】编程题1

数据库SQL命令测试题1 10-1 显示教工编号以02开头的教师信息 作者 冰冰 单位 广东东软学院 显示教工编号以02开头的教师信息 提示&#xff1a;请使用SELECT语句作答。 表结构: CREATE TABLE teacher ( TId CHAR(5) NOT NULL, -- 教师工号&#xff0c;主键 DId CHAR(2) …

Dockerhub镜像加速

一、背景 dockerhub由于被封锁和站点处于国外的原因&#xff0c;docker pull拉取镜像非常慢&#xff0c;有时候直接都无法拉取。严重妨碍了我们的学习进度以及日常使用。 总结了一些proxy代理的镜像站点&#xff0c;配置之后速度会有明显提升&#xff0c;大家可以参考使用。 二…

Linux: C语言解析域名

在上一篇博客 Linux: C语言发起 DNS 查询报文 中&#xff0c;自己构造 DNS 查询报文&#xff0c;发出去&#xff0c;接收响应&#xff0c;以二进制形式把响应的数据写入文件并进行分析。文章的最后留下一个悬念&#xff0c;就是写代码解析 DNS answer section 部分。本文来完成…

Tri Mode Ethernet MAC IP核详解

本文对 Vivado 的三速 MAC IP 核&#xff08;Tri Mode Ethernet MAC&#xff0c;TEMAC&#xff09;进行介绍。 在自行实现三速以太网 MAC 控制器时&#xff0c;GMII/RGMII 接口可以通过 IDDR、ODDR 原语实现&#xff0c;然而实际使用中自己实现的模块性能不是很稳定&#xff08…

CENTOS7 升级gcc版本

升级gcc版本 CentOS下升级gcc版本有两个途径&#xff0c;一个是添加其他源进行自动升级&#xff0c;一个是手动编译升级&#xff0c;这里先顺便讲下自动升级的两个办法&#xff1a; a. 添加Fedora源 在 /etc/yum.repos.d 目录中添加文件 FedoraRepo.repo &#xff0c;并输入…

VMware虚拟机(Ubuntu或centOS)共享宿主机网络资源

VMware虚拟机(Ubuntu或centOS)共享宿主机网络资源 由于需要在 Linux 环境下进行一些测试工作&#xff0c;于是决定使用 VMware 虚拟化软件来安装 Ubuntu 24.04 .1操作系统。考虑到测试过程中需要访问 Github &#xff0c;要使用Docker拉去镜像等外部网络资源&#xff0c;因此产…

学习日记_20241123_聚类方法(高斯混合模型)续

前言 提醒&#xff1a; 文章内容为方便作者自己后日复习与查阅而进行的书写与发布&#xff0c;其中引用内容都会使用链接表明出处&#xff08;如有侵权问题&#xff0c;请及时联系&#xff09;。 其中内容多为一次书写&#xff0c;缺少检查与订正&#xff0c;如有问题或其他拓展…