HarmonyOS开发实例:【分布式手写板】

news2024/11/28 12:54:01

介绍

本篇Codelab使用设备管理及分布式键值数据库能力,实现多设备之间手写板应用拉起及同步书写内容的功能。操作流程:

  1. 设备连接同一无线网络,安装分布式手写板应用。进入应用,点击允许使用多设备协同,点击主页上查询设备按钮,显示附近设备。
  2. 选择设备确认,若已建立连接,启动对方设备上的手写板应用,否则提示建立连接。输入PIN码建立连接后再次点击查询设备按钮,选择设备提交,启动对方设备应用。
  3. 建立连接前绘制的内容在启动对方设备后同步,此时设备上绘制的内容会在另一端同步绘制。
  4. 点击撤销按钮,两侧设备绘制内容同步撤销。

相关概念

  • [设备管理]:模块提供分布式设备管理能力。
  • [分布式键值数据库]:分布式键值数据库为应用程序提供不同设备间数据库的分布式协同能力。

相关权限

本篇Codelab使用了设备管理及分布式键值数据库能力,需要手动替换full-SDK,并在配置文件module.json5文件requestPermissions属性中添加如下权限:

  • [分布式设备认证组网权限]:ohos.permission.ACCESS_SERVICE_DM。
  • [设备间的数据交换权限]:ohos.permission.DISTRIBUTED_DATASYNC。

约束与限制

  1. 本篇Codelab部分能力依赖于系统API,需下载full-SDK并替换DevEco Studio自动下载的public-SDK。
  2. 本篇Codelab使用的部分API仅系统应用可用,需要提升应用等级。

环境搭建

软件要求

  • [DevEco Studio]版本:DevEco Studio 4.0 Beta2。
  • OpenHarmony SDK版本:API version 10。
  • 鸿蒙指导参考:qr23.cn/AKFP8k点击或者复制转到。

搜狗高速浏览器截图20240326151547.png

硬件要求

鸿蒙HarmonyOS与OpenHarmony文档籽料+mau123789是v直接拿取

  • 开发板类型:[润和RK3568开发板]。
  • OpenHarmony系统:4.0 Release。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. [获取OpenHarmony系统版本]:标准系统解决方案(二进制)。以4.0 Release版本为例:

  2. 搭建烧录环境。

    1. [完成DevEco Device Tool的安装]
    2. [完成RK3568开发板的烧录]
  3. 搭建开发环境。

    1. 开始前请参考[工具准备],完成DevEco Studio的安装和开发环境配置。
    2. 开发环境配置完成后,请参考[使用工程向导]创建工程(模板选择“Empty Ability”)。
    3. 工程创建完成后,选择使用[真机进行调测]。

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。

├──entry/src/main/ets                 // 代码区
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets       // 公共常量类
│  │  └──utils
│  │     ├──Logger.ets                // 日志打印类
│  │     └──RemoteDeviceUtil.ets      // 设备管理类
│  ├──entryability
│  │  └──EntryAbility.ets             // 程序入口类
│  ├──pages
│  │  └──Index.ets                    // 主界面
│  ├──view
│  │  └──CustomDialogComponent.ets    // 自定义弹窗组件类
│  └──viewmodel
│     ├──KvStoreModel.ets             // 分布式键值数据库管理类
│     └──Position.ets                 // 绘制位置信息类
└──entry/src/main/resources           // 资源文件目录

界面设计

主界面由导航栏及绘制区域组成,导航栏包含撤回按钮及查询设备按钮。绘制区域使用Canvas画布组件展示绘制效果。Index.ets文件完成界面实现,使用Column及Row容器组件进行布局。

// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
  ...
  build() {
    Column() {
      Row() {
        // 撤回按钮
        Image($r('app.media.ic_back'))
          .width($r('app.float.ic_back_width'))
          .height($r('app.float.ic_back_height'))
          ...
        Blank()
        // 查找设备按钮
        Image($r('app.media.ic_hop'))
          .width($r('app.float.ic_hop_width'))
          .height($r('app.float.ic_hop_height'))
          ...
      }
      .width(CommonConstants.FULL_PERCENT)
      .height(CommonConstants.TITLE_HEIGHT)

      Row() {
        // 绘制区域
        Canvas(this.canvasContext)
          .width(CommonConstants.FULL_PERCENT)
          .height(CommonConstants.FULL_PERCENT)
          ...
      }
      ...
      .width(CommonConstants.FULL_PERCENT)
      .layoutWeight(CommonConstants.NUMBER_ONE)
    }
    .height(CommonConstants.FULL_PERCENT)
    .width(CommonConstants.FULL_PERCENT)
  }
  ...
}

分布式组网

准备分布式环境

创建设备管理器。设备管理器创建完成后注册设备上线离线监听,信任设备上线离线时触发。执行获取本地设备信息,获取信任设备列表,初始化展示设备列表等方法。其中deviceManager类需使用full-SDK。

// RemoteDeviceUtil.ets
import deviceManager from '@ohos.distributedHardware.deviceManager';

class RemoteDeviceUtil {
  ...
  async createDeviceManager() {
    ...
    await new Promise((resolve: (value: Object | PromiseLike<Object>) => void, reject: ((reason?: RejectError) => void)) => {
      try {
        // 创建设备管理器
        deviceManager.createDeviceManager(CommonConstants.BUNDLE_NAME,
          (error, value: deviceManager.DeviceManager) => {
            ...
            this.myDeviceManager = value;
            // 注册信任设备上线离线监听
            this.registerDeviceStateListener();
            // 获取本地设备信息
            this.getLocalDeviceInfo();
            // 获取信任设备列表
            this.getTrustedDeviceList();
            // 初始化展示设备列表
            this.initDeviceList();
            resolve(value);
        });
      } catch (error) {
        Logger.error('RemoteDeviceModel',
          `createDeviceManager failed, error=${JSON.stringify(error)}`);
      }
    });
  }
  ...
}

注册设备状态监听。已验证设备上线或有新设备验证通过时状态类型为ONLINE,将设备添加至信任设备列表。设备离线时状态类型为OFFLINE,将设备从信任列表中移除。

// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
  ...
  // 注册设备状态改变监听
  registerDeviceStateListener(): void {
    ...
    try {
      // 注册监听
      this.myDeviceManager.on('deviceStateChange', (data) => {
        ...
        switch (data.action) {
          // 设备上线
          case deviceManager.DeviceStateChangeAction.ONLINE: {
            this.deviceStateChangeActionOnline(data.device);
            break;
          }
          // 设备离线
          case deviceManager.DeviceStateChangeAction.OFFLINE: {
            this.deviceStateChangeActionOffline(data.device);
            break;
          }
          ...
        }
      });
    } catch (error) {
      Logger.error('RemoteDeviceModel',
        `registerDeviceStateListener on('deviceStateChange') failed, error=${JSON.stringify(error)}`);
    }
  }

  // 设备上线,加入信任列表及展示列表
  deviceStateChangeActionOnline(device: deviceManager.DeviceInfo): void {
    this.trustedDeviceList[this.trustedDeviceList.length] = device;
    this.addToDeviceList(device);
  }

  // 设备下线,将设备移出信任列表和展示列表
  deviceStateChangeActionOffline(device: deviceManager.DeviceInfo): void {
    let list: deviceManager.DeviceInfo[] = [];
    for (let i: number = 0; i < this.trustedDeviceList.length; i++) {
      if (this.trustedDeviceList[i].networkId !== device.networkId) {
        list.push(this.trustedDeviceList[i]);
        continue;
      }
    }
    this.deleteFromDeviceList(device);
    this.trustedDeviceList = list;
  }
  ...
}

建立分布式连接

点击主界面的查询设备按钮,执行发现设备方法,注册设备发现监听任务,同时拉起弹窗展示设备列表。当弹窗关闭时,执行停止发现设备方法,注销监听任务。

// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
  ...
  // 处理新发现的设备
  deviceFound(data: DeviceInfoInterface): void {
    for (let i: number = 0; i < this.discoverList.length; i++) {
      if (this.discoverList[i].deviceId === data.device.deviceId) {
        Logger.info('RemoteDeviceModel', `deviceFound device exist=${JSON.stringify(data)}`);
        return;
      }
    }
    this.discoverList[this.discoverList.length] = data.device;
    this.addToDeviceList(data.device);
  }

  startDeviceDiscovery(): void {
    ...
    try {
      // 注册发现设备监听
      this.myDeviceManager.on('deviceFound', (data) => {
        ...
        // 处理发现的设备
        this.deviceFound(data);
      });
      ...
      let info: deviceManager.SubscribeInfo = {
        subscribeId: this.subscribeId,
        mode: CommonConstants.SUBSCRIBE_MODE,
        medium: CommonConstants.SUBSCRIBE_MEDIUM,
        freq: CommonConstants.SUBSCRIBE_FREQ,
        isSameAccount: false,
        isWakeRemote: true,
        capability: CommonConstants.SUBSCRIBE_CAPABILITY
      };
      // 发现周边设备
      this.myDeviceManager.startDeviceDiscovery(info);
    } catch (error) {
      Logger.error('RemoteDeviceModel',
        `startDeviceDiscovery failed error=${JSON.stringify(error)}`);
    }
  }

  // 停止发现设备
  stopDeviceDiscovery(): void {
    ...
    try {
      // 停止发现设备
      this.myDeviceManager.stopDeviceDiscovery(this.subscribeId);
      // 注销监听任务
      this.myDeviceManager.off('deviceFound');
      this.myDeviceManager.off('discoverFail');
    } catch (error) {
      Logger.error('RemoteDeviceModel',
        `stopDeviceDiscovery failed error=${JSON.stringify(error)}`);
    }
  }
  ...
}

选择弹窗内的设备项提交后,执行设备验证。

  1. 若设备在信任设备列表,执行startAbility()方法启动连接设备上的应用,将当前的绘制信息作为参数发送至连接设备。
  2. 若设备不是信任设备,执行authenticateDevice()方法启动验证。此时连接设备提示是否接受,接收连接后连接设备展示PIN码,本地设备输入PIN码确认后连接成功。再次点击查询设备按钮,选择已连接设备,点击确认启动连接设备上的应用。
// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
  ...
  // 设备验证
  authenticateDevice(
    context: common.UIAbilityContext,
    device: deviceManager.DeviceInfo,
    positionList: Position[]
  ): void {
    // 设备为信任设备,启动连接设备上的应用
    let tmpList = this.trustedDeviceList.filter((item: deviceManager.DeviceInfo) => device.deviceId === item.deviceId);
    if (tmpList.length > 0) {
      this.startAbility(context, device, positionList);
      return;
    }
    ...
    try {
      // 执行设备认证,启动验证相关弹窗,接受信任,显示PIN码,输入PIN码等
      this.myDeviceManager.authenticateDevice(device, authParam, (err) => {
        ...
      })
    } catch (error) {
      Logger.error('RemoteDeviceModel',
        `authenticateDevice failed error=${JSON.stringify(error)}`);
    }
  }

  // 启动连接设备上的应用
  startAbility(context: common.UIAbilityContext, device: deviceManager.DeviceInfo, positionList: Position[]): void {
    ...
    // 启动连接设备上的应用
    context.startAbility(wantValue).then(() => {
      Logger.info('RemoteDeviceModel', `startAbility finished wantValue=${JSON.stringify(wantValue)}`);
    }).catch((error: Error) => {
      Logger.error('RemoteDeviceModel', `startAbility failed, error=${JSON.stringify(error)}`);
    })
  }
  ...
}

资源释放

程序关闭时,注销设备状态监听任务,并释放DeviceManager实例。

// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
  ...
  // 注销监听任务
  unregisterDeviceListCallback(): void {
    ...
    try {
      // 注销设备状态监听
      this.myDeviceManager.off('deviceStateChange');
      // 释放DeviceManager实例
      this.myDeviceManager.release();
    } catch (err) {
      Logger.error('RemoteDeviceModel',
        `unregisterDeviceListCallback stopDeviceDiscovery failed, error=${JSON.stringify(err)}`);
    }
  }
  ...
}

绘制功能

Canvas组件区域监听触摸事件,按照按下、移动、抬起等触摸事件,记录绘制的起点、中间点以及终点。触摸事件触发时,使用CanvasRenderingContext2D对象的绘制方法根据位置信息进行绘制。绘制结束后,将当前位置信息列表存入分布式键值数据库。

// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
  ...  
  build() {
    Column() {
      ...
      Row() {
        Canvas(this.canvasContext)
          ...
      }
      .onTouch((event: TouchEvent) => {
        this.onTouchEvent(event);
      })
      ...
    }
    ...
  }

  // 绘制事件
  onTouchEvent(event: TouchEvent): void {
    let positionX: number = event.touches[0].x;
    let positionY: number = event.touches[0].y;
    switch (event.type) {
      // 手指按下
      case TouchType.Down: {
        this.canvasContext.beginPath();
        this.canvasContext.lineWidth = CommonConstants.CANVAS_LINE_WIDTH;
        this.canvasContext.lineJoin = CommonConstants.CANVAS_LINE_JOIN;
        this.canvasContext.moveTo(positionX, positionY);
        this.pushData(true, false, positionX, positionY);
        break;
      }
      // 手指移动
      case TouchType.Move: {
        this.canvasContext.lineTo(positionX, positionY);
        this.pushData(false, false, positionX, positionY);
        break;
      }
      // 手指抬起
      case TouchType.Up: {
        this.canvasContext.lineTo(positionX, positionY);
        this.canvasContext.stroke();
        this.pushData(false, true, positionX, positionY);
        break;
      }
      default: {
        break;
      }
    }
  }

  pushData(isFirstPosition: boolean, isEndPosition: boolean, positionX: number, positionY: number): void {
    let position = new Position(isFirstPosition, isEndPosition, positionX, positionY);
    // 存入位置信息列表
    this.positionList.push(position);
    if (position.isEndPosition) {
      // 当前位置为终点时,将位置信息列表存入分布式键值数据库
      this.kvStoreModel.put(CommonConstants.CHANGE_POSITION, JSON.stringify(this.positionList));
    }
  }
  ...
}

点击撤销按钮时,从位置列表中后序遍历移除位置信息,直到找到轨迹的初始位置,完成移除上一次绘制的轨迹。移除完成后将位置信息列表存入分布式键值数据库中。执行redraw()方法,清空画板上的内容,遍历位置信息列表,重新绘制。

// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
  ...
  @LocalStorageProp('positionList') positionList: Position[] = [];
  ...
  build() {
    Column() {
      Row() {
        // 撤销按钮
        Image($r('app.media.ic_back'))
          .width($r('app.float.ic_back_width'))
          .height($r('app.float.ic_back_height'))
          .margin({ left: CommonConstants.ICON_MARGIN_LEFT })
          .onClick(() => {
            this.goBack();
          })
        ...
      }
      .width(CommonConstants.FULL_PERCENT)
      .height(CommonConstants.TITLE_HEIGHT)
      ...
  }

  ...
  redraw(): void {
    // 删除画布内的绘制内容
    this.canvasContext.clearRect(0, 0, this.canvasContext.width, this.canvasContext.height);
    // 使用当前记录的位置信息,重新绘制
    this.positionList.forEach((position) => {
      ...
      if (position.isFirstPosition) {
        this.canvasContext.beginPath();
        this.canvasContext.lineWidth = CommonConstants.CANVAS_LINE_WIDTH;
        this.canvasContext.lineJoin = CommonConstants.CANVAS_LINE_JOIN;
        this.canvasContext.moveTo(position.positionX, position.positionY);
      } else {
        this.canvasContext.lineTo(position.positionX, position.positionY);
        if (position.isEndPosition) {
          this.canvasContext.stroke();
        }
      }
    });
  }


  // 撤回上一笔绘制
  goBack(): void {
    if (this.positionList.length === 0) {
      return;
    }
    // 移除位置信息直到位置起始位置
    for (let i: number = this.positionList.length - 1; i >= 0; i--) {
      let position: Position | undefined = this.positionList.pop();
      if (position !== undefined && position.isFirstPosition) {
        break;
      }
    }
    this.redraw();
    this.kvStoreModel.put(CommonConstants.CHANGE_POSITION, JSON.stringify(this.positionList));
  }
  ...
}

分布式键值数据库

使用分布式键值数据库需申请数据交换权限:ohos.permission.DISTRIBUTED_DATASYNC。

应用启动时创建分布式键值数据库,设置数据库数据改变监听。数据改变时执行回调,获取插入或更新数据列表,遍历列表,匹配位置信息列表的设置key,更新位置列表后重新绘制。

// Index.ets
...
import KvStoreModel from '../viewmodel/KvStoreModel';
...
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
  ...
  private kvStoreModel: KvStoreModel = new KvStoreModel();
  ...
  aboutToAppear() {
    ...
    this.createKVStore();
  }

  ...
  createKVStore(): void {
    // 创建分布式键值数据库
    this.kvStoreModel.createKvStore(this.context, (data: distributedKVStore.ChangeNotification) => {
      // 使用分布式键值数据库内的内容重置位置信息列表
      this.positionList = [];
      let entries: distributedKVStore.Entry[] = data.insertEntries.length > 0 ? data.insertEntries : data.updateEntries;
      entries.forEach((entry: distributedKVStore.Entry) => {
        if (CommonConstants.CHANGE_POSITION === entry.key) {
          this.positionList = JSON.parse((entry.value.value) as string);
          // 位置信息列表更新后,重新绘制
          this.redraw();
        }
      });
    });
  }
  ...
}

创建分布式键值数据库。设置数据库类型为KVStoreType.SINGLE_VERSION单版本数据库,其他配置参考[创建数据库配置信息]。创建数据库成功后,调用enableSync()方法开启同步,调用setDataChangeListener()方法订阅数据变更通知。

// KvStoreModel.ets
export default class KvStoreModel {
  ...
  kvStore?: distributedKVStore.SingleKVStore;
  ...
  createKvStore(
    context: common.UIAbilityContext,
    callback: (data: distributedKVStore.ChangeNotification) => void
  ): void {
    ...
    try {
      // 创建一个KVManager对象实例,用于管理数据库对象
      this.kvManager = distributedKVStore.createKVManager(config);
    } catch (error) {
      Logger.error('KvStoreModel',
        `createKvStore createKVManager failed, err=${JSON.stringify(error)}`);
      return;
    }

    // 创建数据库的配置信息
    let options: distributedKVStore.Options = {
      ...
      kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION
      ...
    };

    // 获取分布式键值数据库
    this.kvManager.getKVStore(CommonConstants.KVSTORE_ID, options).then((store: distributedKVStore.SingleKVStore) => {
      ...
      this.kvStore = store;
      // 开启同步
      this.kvStore.enableSync(true).then(() => {
        Logger.info('KvStoreModel', 'createKvStore enableSync success');
      }).catch((error: Error) => {
        Logger.error('KvStoreModel',
          `createKvStore enableSync fail, error=${JSON.stringify(error)}`);
      });
      this.setDataChangeListener(callback);
    }).catch((error: Error) => {
      Logger.error('getKVStore',
        `createKvStore getKVStore failed, error=${JSON.stringify(error)}`);
    })
  }
  ...
}

订阅数据变更通知。创建分布式键值数据库,设置数据变更订阅,订阅类型为全部,当更新数据集或插入数据集大于0时,执行传入的callback()方法。

// KvStoreModel.ets
export default class KvStoreModel {
  ...
  kvStore?: distributedKVStore.SingleKVStore;
  ...
  setDataChangeListener(callback: (data: distributedKVStore.ChangeNotification) => void): void {
    ...
    try {
      // 订阅数据变更通知
      this.kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL,
        (data: distributedKVStore.ChangeNotification) => {
          if ((data.updateEntries.length > 0) || (data.insertEntries.length > 0)) {
            callback(data);
          }
        });
    } catch (error) {
      Logger.error('KvStoreModel',
        `setDataChangeListener on('dataChange') failed, err=${JSON.stringify(error)}`);
    }
  }
  ...
}

应用退出时,分布式键值数据库取消数据改变监听。

// Index.ets
...
import KvStoreModel from '../viewmodel/KvStoreModel';
...
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
  ...
  private kvStoreModel: KvStoreModel = new KvStoreModel();
  ...
  aboutToDisappear() {
    this.kvStoreModel.removeDataChangeListener();
  }
  ...
}

// KvStoreModel.ets
export default class KvStoreModel {
  ...
  kvStore?: distributedKVStore.SingleKVStore;
  ...
  removeDataChangeListener(): void {
    ...
    try {
      // 取消数据改变监听
      this.kvStore.off('dataChange');
    } catch (error) {
      Logger.error('KvStoreModel',
        `removeDataChangeListener off('dataChange') failed, err=${JSON.stringify(error)}`);
    }
  }
  ...
}

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

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

相关文章

接口压力测试 jmeter--入门篇(一)

一 压力测试的目的 评估系统的能力识别系统的弱点&#xff1a;瓶颈/弱点检查系统的隐藏的问题检验系统的稳定性和可靠性 二 性能测试指标以及测算 【虚拟用户数】&#xff1a;线程用户【并发数】&#xff1a;指在某一时间&#xff0c;一定数量的虚拟用户同时对系统的某个功…

如何使用 ArcGIS Pro 制作边界晕渲效果

在某些出版的地图中&#xff0c;边界有类似于“发光”的晕渲效果&#xff0c;这里为大家介绍一下如何使用ArcGIS Pro 制作这种晕渲效果&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的行政区划数据&#xff0c;除了行政区划数据&#xff0c…

【C++进阶】C++中的继承

一、概述 作为C的三大特性之一封装&#xff0c;继承&#xff0c;多态 中的继承&#xff0c;我们在进阶部分一定要详细说明。请跟着如下的小标题进入深度学习。 二、正文 1.继承的概念及定义 首先&#xff0c;我们先要知道什么是继承&#xff0c; 继承 (inheritance)机制是面…

Unity之OpenXR+XR Interaction Toolkit快速监听手柄任意按键事件

前言 当我们开发一个VR时,有时希望监听一个手柄按键的点击事件,或者一个按钮的Value值等。但是每次有可能监听的按钮有不一样,有可能监听的值不一样,那么每次这么折腾,有点累了,难道就没有一个万能的方法,让我可以直接监听我想要的某个按钮的事件么? 答案是肯定的,今…

vscode 搭建stm32开发环境记录(eide+cortex-debug+jlink)

前言 clion使用的快过期了&#xff0c;所以就准备使用vscode 来代替clion作为代码开发环境 vscode 插件安装 创建个空白工程 添加项目相关的源文件&#xff0c;和配置宏定义和头文件目录 编译和烧录(ok) 结合cortex-debug 结果(测试ok)

Prometheus + Grafana 搭建监控仪表盘

目标要求 1、需要展现的仪表盘&#xff1a; SpringBoot或JVM仪表盘 Centos物理机服务器&#xff08;实际为物理分割的虚拟服务器&#xff09;仪表盘 2、展现要求: 探索Prometheus Grafana搭建起来的展示效果&#xff0c;尽可能展示能展示的部分。 一、下载软件包 监控系统核心…

政安晨:【深度学习神经网络基础】(十一)—— 激活函数的导数以及在反向传播中的应用

目录 线性激活函数的导数 Softmax激活函数的导数 S型激活函数的导数 双曲正切激活函数的导数 ReLU激活函数的导数 如何在反向传播中应用 批量训练和在线训练 随机梯度下降 反向传播权重更新 选择学习率和动量 Nesterov动量 政安晨的个人主页&#xff1a;政安晨 欢迎…

2024年MathorCup数学建模C题物流网络分拣中心货量预测及人员排班解题文档与程序

2024年第十四届MathorCup高校数学建模挑战赛 C题 物流网络分拣中心货量预测及人员排班 原题再现&#xff1a; 电商物流网络在订单履约中由多个环节组成&#xff0c;图1是一个简化的物流网络示意图。其中&#xff0c;分拣中心作为网络的中间环节&#xff0c;需要将包按照不同流…

循环神经网络(RNN):概念、挑战与应用

循环神经网络&#xff08;RNN&#xff09;&#xff1a;概念、挑战与应用 1 引言 1.1 简要回顾 RNN 在深度学习中的位置与重要性 在深度学习的壮丽图景中&#xff0c;循环神经网络&#xff08;Recurrent Neural Networks&#xff0c;RNN&#xff09;占据着不可或缺的地位。自从…

C++如何使用string类

文章目录 为什么要学习string?库中的string关于编码ASCII编码Unicode编码 迭代器Iteratorsstring常用构造接口接口声明与功能说明接口演示 string类对象的容量操作接口声明与功能说明接口演示reverse与resize在不同平台下的扩容与缩容机制 string类对象的访问及遍历操作接口声…

stm32中的中断优先级

在工作中使用到多个定时器中断,由于中断的中断优先级不熟悉导致出错,下面来写一下中断的一些注意事项。 一、中断的分类 1、EXTI外部中断:由外部设备或外部信号引发,例如按键按下、外部传感器信号变化等。外部中断用于响应外部事件,并及时处理相关任务。 2、内部中断:…

3DGS渐进式渲染 - 离线生成渲染视频

总览 输入&#xff1a;环绕Object拍摄的RGB视频 输出&#xff1a;自定义相机路径的渲染视频&#xff08;包含渐变效果&#xff09; 实现过程 首先&#xff0c;编译3DGS的C代码&#xff0c;并跑通convert.py、train.py和render.py。教程如下&#xff1a; github网址&#xf…

如何安装 IntelliJ IDEA 最新版本——详细教程

IntelliJ IDEA 简称 IDEA&#xff0c;被业界公认为最好的 Java 集成开发工具&#xff0c;尤其在智能代码助手、代码自动提示、代码重构、代码版本管理(Git、SVN、Maven)、单元测试、代码分析等方面有着亮眼的发挥。IDEA 产于捷克&#xff0c;开发人员以严谨著称的东欧程序员为主…

利用大语言模型,矢量数据库实现数据库的智能搜索

目的 数据库使用SQL 语言查询数据&#xff0c;数据库的记录中要有一个关键字段&#xff08;通常称为主键字段&#xff0c;它的值在数据库列表中是唯一的&#xff09;,数据记录是结构化的. 如果你需要根据数据记录的内容来查询数据记录&#xff0c;就需要通过Select 语句在数据库…

SQL255 给出employees表中排名为奇数行的first_name

题目来源&#xff1a; 给出employees表中排名为奇数行的first_name_牛客题霸_牛客网 描述 对于employees表中&#xff0c;输出first_name排名(按first_name升序排序)为奇数的first_name CREATE TABLE employees ( emp_no int(11) NOT NULL, birth_date date NOT NULL, firs…

春藤实业启动SAP S/4HANA Cloud Public Edition项目,与工博科技携手数字化转型之路

3月11日&#xff0c;广东省春藤实业有限公司&#xff08;以下简称“春藤实业”&#xff09;SAP S/4HANA Cloud Public Edition&#xff08;以下简称“SAP ERP公有云”&#xff09;项目正式启动。春藤实业董事长陈董、联络协调项目经理慕总、内部推行项目经理陈总以及工博董事长…

【.Net动态Web API】背景与实现原理

&#x1f680;前言 本文是《.Net Core进阶编程课程》教程专栏的导航站&#xff08;点击链接&#xff0c;跳转到专栏主页&#xff0c;欢迎订阅&#xff0c;持续更新…&#xff09; 专栏介绍&#xff1a;通过源码实例来讲解Asp.Net Core进阶知识点&#xff0c;让大家完全掌握每一…

pt-archiver归档表数据

一 介绍 pt-archiver的原理主要是根据定义的时间间隔(sleep参数)&#xff0c;扫描要清理的数据表。它按照指定的规则分批(limit参数)将查询到的记录转移到其他表或文件中&#xff0c;发现它是按主键去删除的表数据&#xff0c;对数据库影响很小。 二 语法 /bin/pt-archiver …

关于外网java后端服务访问内网minio中间件,因连接minio超时,启动失败问题

注&#xff1a;服务器情况&#xff1a;2台服务器&#xff0c;内网服务器包含&#xff08;activemq、minio、nginx、redis、mysql、后端java服务&#xff09;。外网服务器只有后端java服务&#xff0c;访问内网的中间件&#xff08;内网服务器开放了部分指定端口&#xff09; 问…

GPT状态和原理 - 解密OpenAI模型训练

目录 1 如何训练 GPT 助手 1.1 第一阶段 Pretraining 预训练 1.2 第二阶段&#xff1a;Supervised Finetuning有监督微调 1.3 第三阶段 Reward Modeling 奖励建模 1.4 第四阶段 Reinforcement Learning 强化学习 1.5 总结 2 第二部分&#xff1a;如何有效的应用在您的应…