結課作業自選01. 內核空間 MPU6050 體感鼠標驅動程序(二)(完整實現流程)

目錄

一. 題目要求-內核空間 MPU6050 體感鼠標驅動程序

二. 偽代碼及程序運行流程

三. 主要函數詳解(根據代碼流程進行詳解)

3.1?module_i2c_driver宏(對應“1”)

3.2?mpu_of_match設備樹匹配表(對應“2”)

3.3?MODULE_DEVICE_TABLE宏聲明驅動支持的設備列表

3.4?mpu_mouse_probe驅動檢測函數(初始化設備)(對應“3”)

3.4.1?init_gpio_reg初始化GPIO時鐘和寄存器映射

3.5 timer_callback回調函數(對應“4、5”)

3.6 accel_work_handler 工作隊列處理函數(定時讀取數據)(對應“6、7”)

3.6.1?read_accel加速度讀取函數

3.6.1.1 i2c_smbus_read_i2c_block_data函數功能

3.6.1.2 convert_accel函數 轉換加速度為位移

3.6.2?lowpass_filter低通濾波函數

四. 完整版代碼


一. 題目要求-內核空間 MPU6050 體感鼠標驅動程序

????????(1)采用課上練習“設備驅動練習”給出的 i2c 框架程序

????????(2)修改 dts 文件時,按照課件中的描述修改。

????????(3)實現驅動模型要求的 probe()函數。

注意:

????????鼠標功能除了 MPU6050 運動傳感器外還需要一個或兩個按鍵(左右鍵),

????????按鍵可以使用按鍵中斷或者使用 MPU6050 定時器同步讀取電平。????????

????????自己設計濾波程序使鼠標指針穩定。

????????(4)實現 mpu6050 的定時服務函數,并向 input 核心層報告事件。可以自己選擇實

現鼠標類設備還是觸摸屏類設備。

????????(5)編寫一個應用程序來測試驅動,讀出鼠標坐標值和按鍵事件。

二. 偽代碼及程序運行流程

? ? ? ? 代碼執行流程和前一個博客中,內核使用mpu6050的流程一致,此代碼就是在前代碼的基礎上修改完成的。

????????1. 模塊加載insmod mpu6050_kernel.ko時module_i2c_driver宏會自動注冊

????????2. mpu_mouse_driver結構體里的.probe對應的mpu_mouse_probe在I2C總線檢測到匹配的設備時執行(根據mpu_mouse_driver結構體中的.of_match_table設備樹匹配表進行匹配檢測)

????????3. mpu_mouse_probe在進行一系列設備初始化之后執行mod_timer函數定時器計時

????????4. 定時器到期之后執行定時器回調函數timer_callback

????????5. timer_callback里面執行data->work,也就是accel_work_handler工作隊列處理函數

????????6. accel_work_handler函數讀取加速度并傳給虛擬鼠標控制鼠標實現體感鼠標功能

? ? ? ? 7. accel_work_handler函數最后執行mod_timer函數,重新給定時器計時,然后回到4.一直循環執行,直到rmmod mpu6050_kernel.ko為止

// mpu_mouse_kernel.cstruct mpu_mouse_data {// 代碼中用到的主要變量
};/* 加速度轉換函數(純整數運算) */
static void convert_accel(int16_t raw_x, int16_t raw_y, int *dx, int *dy) {// 將加速度轉換成鼠標的位移
}/*  低通濾波函數:filtered_val = (new_val + 3*last)/4 */
static void lowpass_filter(int *filtered_val, int new_val) {// 低通濾波
}/* 讀取加速度計數據 */
static void read_accel(struct i2c_client *client, int *dx, int *dy) {// 從MPU6050寄存器讀取原始數據(加速度XYZ)i2c_smbus_read_i2c_block_data(client, 0x3B, 6, buf);// 轉換加速度為位移(注意,這里是將raw_y傳給了x, raw_x傳給了y, 因為mpu和屏幕的xy軸是相反的)convert_accel(raw_y, raw_x, dx, dy);
}/* 新增:初始化GPIO時鐘和寄存器映射(基于gpios.c中的myopen函數邏輯) */
static void init_gpio_reg(struct mpu_mouse_data *data) {// 映射APER_CLK并啟用GPIO時鐘(復用gpios.c中的邏輯)// 映射GPIO_DATA2寄存器(復用gpios.c中的邏輯)
}// 6. accel_work_handler函數讀取加速度并傳給虛擬鼠標控制鼠標實現體感鼠標功能
// 7. accel_work_handler函數最后執行mod_timer函數,重新給定時器計時,然后回到4.一直循環執行,直到rmmod mpu6050_kernel.ko為止
/* 修改:在工作隊列處理函數中添加按鍵檢測(新增代碼) */
/* 工作隊列處理函數(定時讀取數據) */
static void accel_work_handler(struct work_struct *work) {// 讀取加速度及按鍵事件并上報// 重新調度定時器mod_timer(&data->timer, jiffies + msecs_to_jiffies(SAMPLE_INTERVAL));
}// 5. timer_callback里面執行data->work,也就是accel_work_handler工作隊列處理函數
/* 定時器回調函數(觸發工作隊列) */
static void timer_callback(struct timer_list *t) {struct mpu_mouse_data *data = from_timer(data, t, timer);schedule_work(&data->work);
}// 3. mpu_mouse_probe在進行一系列設備初始化之后執行mod_timer函數定時器倒計時
// 4. 定時器到期之后執行定時器回調函數timer_callback
/* 修改:在probe函數中初始化GPIO(新增代碼) */
/* 驅動探測函數(初始化設備) */
static int mpu_mouse_probe(struct i2c_client *client, const struct i2c_device_id *id) {// 設備初始化// 初始化定時器和工作隊列timer_setup(&data->timer, timer_callback, 0);INIT_WORK(&data->work, accel_work_handler);mod_timer(&data->timer, jiffies + msecs_to_jiffies(SAMPLE_INTERVAL));return 0;
}/* 修改:在remove函數中釋放GPIO映射(新增代碼) */
/* 驅動移除函數(釋放資源) */
static int mpu_mouse_remove(struct i2c_client *client) {// +++ 新增:取消GPIO寄存器映射// 清理定時器和工作隊列
}/* 設備樹匹配表 */
static const struct of_device_id mpu_of_match[] = {{ .compatible = "inv,mpu6050" },    // 必須與設備樹中的compatible字段一致{ }
};
MODULE_DEVICE_TABLE(of, mpu_of_match);// 2. mpu_mouse_driver結構體里的.probe對應的mpu_mouse_probe在I2C總線檢測到匹配的設備時執行
/* I2C驅動結構體 */
static struct i2c_driver mpu_mouse_driver = {.probe = mpu_mouse_probe,.remove = mpu_mouse_remove,.driver = {.name = "mpu6050-mouse",.of_match_table = mpu_of_match,     // 啟用設備樹匹配},
};// 1. 模塊加載insmod mpu6050_kernel.ko時module_i2c_driver宏會自動注冊
module_i2c_driver(mpu_mouse_driver);
MODULE_DESCRIPTION("MPU6050 I2C Mouse Driver with GPIO Buttons");
MODULE_LICENSE("GPL");

三. 主要函數詳解(根據代碼流程進行詳解)

3.1?module_i2c_driver宏(對應“1”)

????????1. 模塊加載insmod mpu6050_kernel.ko時module_i2c_driver宏會自動注冊

? ? ? ? 1. 自動生成模塊的加載/卸載函數

????????開發者只需定義一個?i2c_driver?結構體,并通過?module_i2c_driver?宏將其綁定,即可自動生成以下代碼:

module_init(i2c_driver_probe);  // 模塊加載時調用 i2c_add_driver
module_exit(i2c_driver_remove); // 模塊卸載時調用 i2c_del_driver

????????無需手動編寫?module_init?和?module_exit

? ? ? ? 2. 封裝驅動注冊與注銷

????????宏內部通過調用?i2c_add_driver?和?i2c_del_driver?完成以下操作:

????????注冊驅動:將?i2c_driver?注冊到內核的 I2C 子系統。

????????注銷驅動:在模塊卸載時,安全地移除驅動并釋放資源。

? ? ? ? 3. 本代碼解釋

static struct i2c_driver mpu_mouse_driver = {.probe = mpu_mouse_probe,.remove = mpu_mouse_remove,.driver = {.name = "mpu6050-mouse",.of_match_table = mpu_of_match,},
};
module_i2c_driver(mpu_mouse_driver);

????????注冊流程
????????當通過?insmod?加載驅動模塊時,module_i2c_driver?會自動調用?i2c_add_driver(&mpu_mouse_driver),觸發設備探測(.probe?函數)。

????????注銷流程
????????當通過?rmmod?卸載模塊時,自動調用?i2c_del_driver(&mpu_mouse_driver),執行?.remove?函數清理資源。

????????driver.name:驅動名稱(需唯一)。

????????of_match_table(可選):設備樹匹配表。

3.2?mpu_of_match設備樹匹配表(對應“2”)

????????2. mpu_mouse_driver結構體里的.probe對應的mpu_mouse_probe在I2C總線檢測到匹配的設備時執行(根據mpu_mouse_driver結構體中的.of_match_table設備樹匹配表進行匹配檢測)

? ? ? ? 設備樹文件中i2c連接mpu6050的compatible?字段如下,mpu_of_match中的compatible?字段與設備樹中的compatible?字段相同,即為匹配成功,然后執行mpu_mouse_probe函數。

3.3?MODULE_DEVICE_TABLE宏聲明驅動支持的設備列表

? ?MODULE_DEVICE_TABLE?是 Linux 內核中一個關鍵的宏,用于?聲明驅動支持的設備列表,并幫助內核實現模塊與設備的動態匹配。以下是其具體作用和實現細節:?

static const struct of_device_id mpu_of_match[] = {{ .compatible = "inv,mpu6050" }, // 與設備樹節點中的 compatible 字段匹配{ }
};
MODULE_DEVICE_TABLE(of, mpu_of_match); // 關鍵宏聲明

具體流程

????????模塊加載時

? ?1. 內核解析模塊中的?MODULE_DEVICE_TABLE(of, ...),將兼容性字符串(如?"inv,mpu6050")注冊到全局設備樹匹配表。

????????設備樹解析時

? ? ? ? 2. 內核啟動時,設備樹中的節點若包含?compatible = "inv,mpu6050",則會觸發匹配邏輯。

????????驅動綁定

? ? ? ? 3. 內核調用匹配驅動的?.probe?函數(即?mpu_mouse_probe),完成硬件初始化。

3.4?mpu_mouse_probe驅動檢測函數(初始化設備)(對應“3”)

????????3. mpu_mouse_probe在進行一系列設備初始化之后執行mod_timer函數定時器計時

/* 修改:在probe函數中初始化GPIO(新增代碼) */
/* 驅動探測函數(初始化設備) */
static int mpu_mouse_probe(struct i2c_client *client, const struct i2c_device_id *id) {struct device *dev = &client->dev;  // 獲取與當前 I2C 設備關聯的通用設備結構體指針struct mpu_mouse_data *data;int ret;// 分配設備數據結構data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);if (!data) return -ENOMEM;// 初始化I2C客戶端data->client = client;i2c_set_clientdata(client, data);// 初始化輸入設備data->input = devm_input_allocate_device(dev);if (!data->input) return -ENOMEM;data->input->name = "MPU6050 Mouse";data->input->id.bustype = BUS_I2C;// !!! 修改:注冊按鍵事件類型(新增EV_KEY支持)__set_bit(EV_REL, data->input->evbit);__set_bit(REL_X, data->input->relbit);__set_bit(REL_Y, data->input->relbit);__set_bit(EV_KEY, data->input->evbit);      // +++ 新增按鍵事件__set_bit(BTN_LEFT, data->input->keybit);   // +++ 左鍵__set_bit(BTN_RIGHT, data->input->keybit);  // +++ 右鍵// 注冊輸入設備ret = input_register_device(data->input);if (ret) {dev_err(dev, "Failed to register input device\n");return ret;}// +++ 新增:初始化GPIO寄存器init_gpio_reg(data);// 初始化MPU6050i2c_smbus_write_byte_data(client, 0x1C, 0x00);i2c_smbus_write_byte_data(client, 0x6B, 0x00);msleep(100);data->filtered_dx = 0;data->filtered_dy = 0;// +++ 新增:初始化按鍵狀態和去抖動計數器data->prev_left_state = 1; // 默認未按下data->prev_right_state = 1;// 初始化定時器和工作隊列timer_setup(&data->timer, timer_callback, 0);INIT_WORK(&data->work, accel_work_handler);mod_timer(&data->timer, jiffies + msecs_to_jiffies(SAMPLE_INTERVAL));dev_info(dev, "MPU6050 Mouse Driver Initialized\n");return 0;
}

3.4.1?init_gpio_reg初始化GPIO時鐘和寄存器映射

? ? ? ? 仿照gpio內核驅動代碼改寫

/* 新增:初始化GPIO時鐘和寄存器映射(基于gpios.c中的myopen函數邏輯) */
static void init_gpio_reg(struct mpu_mouse_data *data) {unsigned int *clk_reg;// 映射APER_CLK并啟用GPIO時鐘(復用gpios.c中的邏輯)clk_reg = ioremap(APER_CLK, 4);if (clk_reg) {iowrite32(ioread32(clk_reg) | 1 << 22, clk_reg); // 設置第22位iounmap(clk_reg);} else {pr_err("Failed to map APER_CLK register\n");}// 映射GPIO_DATA2寄存器(復用gpios.c中的邏輯)data->gpio_reg = ioremap(GPIO_DATA2, 4);if (!data->gpio_reg) {pr_err("Failed to map GPIO_DATA2 register\n");}
}

3.5 timer_callback回調函數(對應“4、5”)

????????4. 定時器到期之后執行定時器回調函數timer_callback

????????5. timer_callback里面執行data->work,也就是accel_work_handler工作隊列處理函數

/* 定時器回調函數(觸發工作隊列) */
static void timer_callback(struct timer_list *t) {struct mpu_mouse_data *data = from_timer(data, t, timer);schedule_work(&data->work);
}

????????schedule_work(&data->work)?的作用是?將工作項?data->work?提交到內核的全局工作隊列中,以便在進程上下文中異步執行?accel_work_handler?函數,確保內核的實時性和穩定性,避免中斷處理被阻塞。

3.6 accel_work_handler 工作隊列處理函數(定時讀取數據)(對應“6、7”)

????????6. accel_work_handler函數讀取加速度并傳給虛擬鼠標控制鼠標實現體感鼠標功能

? ? ? ? 7. accel_work_handler函數最后執行mod_timer函數,重新給定時器計時,然后回到4.一直循環執行,直到rmmod mpu6050_kernel.ko為止

? ? ? ? 按鍵狀態的獲取和判斷也在此函數中

/* 修改:在工作隊列處理函數中添加按鍵檢測(新增代碼) */
/* 工作隊列處理函數(定時讀取數據) */
static void accel_work_handler(struct work_struct *work) {struct mpu_mouse_data *data = container_of(work, struct mpu_mouse_data, work);int dx, dy;uint32_t gpio_state;int current_left, current_right;// 讀取加速度并濾波read_accel(data->client, &dx, &dy);lowpass_filter(&data->filtered_dx, dx);lowpass_filter(&data->filtered_dy, dy);// 上報相對位移事件input_report_rel(data->input, REL_X, data->filtered_dx);input_report_rel(data->input, REL_Y, data->filtered_dy);// +++ 新增:讀取GPIO狀態并上報按鍵事件if (data->gpio_reg) {gpio_state = ioread32(data->gpio_reg);current_left = !(gpio_state & 0x04); // bit2=0表示左鍵按下    current_left=1是按下,=0是未按下current_right = !(gpio_state & 0x02); // bit1=0表示右鍵按下/* 無按鍵按下:37f:0011 0111 0111左按鍵按下:37b:0011 0111 1011右按鍵按下:37d:0011 0111 1101 */// +++ 新增:去抖動邏輯if (current_left != data->prev_left_state || current_right != data->prev_right_state) {// 狀態穩定后上報按鍵事件input_report_key(data->input, BTN_LEFT, current_left);input_report_key(data->input, BTN_RIGHT, current_right);// 更新上一次狀態data->prev_left_state = current_left;data->prev_right_state = current_right;}}input_sync(data->input);// 重新調度定時器mod_timer(&data->timer, jiffies + msecs_to_jiffies(SAMPLE_INTERVAL));
}

3.6.1?read_accel加速度讀取函數

/* 讀取加速度計數據 */
static void read_accel(struct i2c_client *client, int *dx, int *dy) {uint8_t buf[6];int16_t raw_x, raw_y;// 從MPU6050寄存器讀取原始數據(加速度XYZ)i2c_smbus_read_i2c_block_data(client, 0x3B, 6, buf);// 合并高8位和低8位數據raw_x = (buf[0] << 8) | buf[1];raw_y = (buf[2] << 8) | buf[3];// 轉換加速度為位移(注意,這里是將raw_y傳給了x, raw_x傳給了y, 因為mpu和屏幕的xy軸是相反的)convert_accel(raw_y, raw_x, dx, dy);
}
3.6.1.1 i2c_smbus_read_i2c_block_data函數功能

????????指定寄存器地址:通過?reg?參數指定要讀取的寄存器起始地址。

????????封裝位置Linux 內核的?i2c-core-smbus.c?文件。

3.6.1.2 convert_accel函數 轉換加速度為位移

? ? ? ? 因為強制使用浮點運算會導致代碼無法運行,純整數運算確保驅動在不同硬件平臺上的通用性。所以這里使用整數運算,通過設置合理的靈敏度、死區等宏定義參數,確保代碼穩定運行。

/* 加速度轉換函數(純整數運算) */
static void convert_accel(int16_t raw_x, int16_t raw_y, int *dx, int *dy) {// 1. 原始數據轉實際加速度(cm/s2)int ax = (raw_x * GRAVITY_CM_S2) / ACCEL_SCALE_2G;int ay = (raw_y * GRAVITY_CM_S2) / ACCEL_SCALE_2G;// 2. 應用死區濾波ax = (abs(ax) < DEADZONE) ? 0 : ax;ay = (abs(ay) < DEADZONE) ? 0 : ay;// 3. 加速度轉位移(靈敏度調整)*dx = (ax * SENSITIVITY) / 100;  // 整數運算避免浮點*dy = -(ay * SENSITIVITY) / 100; // Y軸方向取反
}

3.6.2?lowpass_filter低通濾波函數

/*  低通濾波函數:filtered_val = (new_val + 3*last)/4 */
static void lowpass_filter(int *filtered_val, int new_val) {*filtered_val = (new_val + 3 * (*filtered_val)) / 4;
}

1. 低通濾波函數的作用

? ? ? ? (1)抑制高頻噪聲
????????通過衰減信號中的快速變化部分(如傳感器噪聲、瞬時干擾),保留低頻成分(如真實運動趨勢),使數據更平滑穩定。

? ? ? ? (2)平滑信號輸出
????????減少測量值的突變,提升數據的可讀性和后續處理的可靠性(如鼠標移動控制)。

2. 為什么使用這個公式(此公式的優勢)

? ? ? ? (1)計算高效

????????僅需?一次乘法、一次加法、一次除法(或位運算),適合實時處理。示例代碼中,除法為整數運算(/4),可優化為右移操作(>> 2),進一步提升速度。

? ? ? ? (2)內存占用極低

????????只需保存?上一次濾波值,無需存儲多組歷史數據(如移動平均濾波需保存N個樣本)。

? ? ? ? (3)參數可調性強

????????通過調整權重比例(如?(new_val + 7 * filtered_val) / 8),可靈活控制平滑效果與響應速度的平衡。

? ? ? ? (4)避免浮點運算

????????純整數運算兼容無FPU的嵌入式平臺,減少內核上下文切換開銷。

? ? ? ? (5)平滑效果顯著

????????在MPU6050驅動中,能有效抑制加速度計的抖動噪聲,使鼠標移動更平滑。

四. 完整版代碼

????????內核空間MPU6050體感鼠標驅動程序資源-CSDN文庫

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/84614.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84614.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/84614.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

5G 核心網切換機制全解析:XN、N2 與移動性注冊對比

摘要 本文深入探討了 5G 核心網中的三種關鍵切換方式:基于 XN 接口的切換、基于 N2 接口的切換以及移動性注冊更新機制。通過對比分析它們的原理、應用場景和技術差異,幫助讀者全面理解 5G 網絡中用戶移動性管理的核心技術。 1. 引言 隨著 5G 技術的廣泛應用,用戶對網絡連…

用深度學習提升DOM解析——自動提取頁面關鍵區塊

一、時間軸&#xff1a;一次“抓不到重點”的二手車數據爬蟲事故 2025/03/18 09:00 產品經理希望抓取懂車帝平臺上“北京地區二手車報價”作為競品監測數據源。我們初步使用傳統XPath方案&#xff0c;試圖提取車型、年限、里程、價格等數據。2025/03/18 10:00 初版腳本運行失敗…

React與Vue的內置指令對比

React 與 Vue 不同&#xff0c;它沒有內置的模板指令系統。React 采用了 JavaScript 優先 的聲明式方式&#xff0c;使用 JSX 語法將 HTML 和 JavaScript 結合在一起。因此&#xff0c;React 中沒有類似 Vue 的 v-if、v-for、v-bind 等內置指令。 React 中的替代方案 條件渲染…

Spring聲明式事務源碼全鏈路剖析與設計模式深度解讀

Spring聲明式事務源碼全鏈路剖析與設計模式深度解讀 作者&#xff1a;AI 日期&#xff1a;2025-05-22 一、前言 Spring事務是企業級開發的基石&#xff0c;但“為什么有時事務失效&#xff1f;”、“不同傳播行為背后發生了什么&#xff1f;”、“Spring事務源碼到底如何實現&…

云原生安全基礎:深入探討容器化環境中的權限隔離與加固策略

&#x1f525;「炎碼工坊」技術彈藥已裝填&#xff01; 點擊關注 → 解鎖工業級干貨【工具實測|項目避坑|源碼燃燒指南】 在云原生環境中&#xff0c;容器化技術&#xff08;如 Docker 和 Kubernetes&#xff09;的廣泛應用帶來了靈活性與效率&#xff0c;但也引入了新的安全挑…

如何在 ONLYOFFICE 演示文稿中調整段落首行縮進

在制作演示文稿時&#xff0c;保持內容的一致性與可讀性至關重要&#xff0c;而段落首行縮進作為格式設置的關鍵環節&#xff0c;直接影響著整體呈現效果。在本文中&#xff0c;我們將介紹如何通過創建 ONLYOFFICE 宏&#xff0c;快速設置演示文稿中所有段落的首行縮進。 關于 …

[Asp.Net]GridView、Repeater 導出Excel長數字顯示成科學計數

類似身份證純數字的格式時 &#xff0c;excel默認是數字格式 變成了科學計數法 &#xff0c; GridView:RowDataBound 添加e.Row.Cells[2].Attributes.Add(“style”, “vnd.ms-excel.numberformat:;”); protected void GridView1_RowDataBound(object sender, GridViewRowE…

Ntfs!NtfsReadBootSector函數分析之nt!CcGetVacbMiss中得到一個nt!_VACB結構

第一部分&#xff1a; 1: kd> g Breakpoint 3 hit nt!CcGetVacbMiss: 80a1a19e 6a30 push 30h 1: kd> kc # 00 nt!CcGetVacbMiss 01 nt!CcGetVirtualAddress 02 nt!CcMapData 03 Ntfs!NtfsMapStream 04 Ntfs!NtfsReadBootSector Ntfs…

Linux10正式版發布,擁抱AI了!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 作者&#xff1a;IT邦德 中國DBA聯盟(ACDU)成員&#xff0c;10余年DBA工作經驗 Oracle、PostgreSQL ACE CSDN博客專家及B站知名UP主&#xff0c;全網粉絲10萬 擅長主流Oracle、MySQL、PG、高斯…

關于 SSE(Server-Sent Events)過程的簡要解剖

Js前端&#xff1a;發送普通請求 fetch(...) .then(()>{}) .catch(()>{})Java后端&#xff1a;接收請求后調用請求處理函數&#xff0c;函數返回一個emiiter對象 public SseEmitter handleRequest(...) {// 創建一個 SseEmitter 對象&#xff0c;用于發送 SSE 事件SseE…

PyTorch 中unsqueeze(-1)用法

unsqueeze(-1) 是 PyTorch 中的一個張量操作&#xff0c;用于?在指定維度上增加一個長度為1的維度?&#xff08;即擴展維度&#xff09;。具體解析如下&#xff1a; 功能說明 ?作用位置? -1 表示在張量的?最后一個維度?后添加新維度。 &#xff08;等價于 dimlen(tensor.…

RTC技術

什么是RTC RTC&#xff08;Real time communication&#xff09;實時通信&#xff0c;是實時音視頻的一個簡稱&#xff0c;我們常說的RTC技術一般指的是WebRTC技術&#xff0c;已經被 W3C 和 IETF 發布為正式標準。由于幾乎所有主流瀏覽器都支持 WebRTC 標準 API &#xff0c;…

vue+cesium示例:3Dtiles三維模型高度調整(附源碼下載)

接到一位知識星友的邀請&#xff0c;實現他需要3Dtiles三維模型的簡單高度調整需求&#xff0c;適合學習Cesium與前端框架結合開發3D可視化項目。 demo源碼運行環境以及配置 運行環境&#xff1a;依賴Node安裝環境&#xff0c;demo本地Node版本:推薦v18。 運行工具&#xff1a;…

詳解3DGS

4 可微分的3D高斯 splatting 核心目標與表示選擇 我們的目標是從無法線的稀疏SfM點出發&#xff0c;優化出一種能夠實現高質量新視角合成的場景表示。為此&#xff0c;我們選擇3D高斯作為基本圖元&#xff0c;它兼具可微分的體表示特性和非結構化的顯式表示優勢&#xff0c;既…

構建版本沒mac上傳APP方法

在蘋果開發者的app store connect上架Ios應用的時候&#xff0c;發現需要使用xode等軟件來上傳iOS的APP。 但是不管是xcode也好&#xff0c;transporter也好&#xff0c;還是命令行工具也好&#xff0c;都必須安裝在mac電腦才能使用&#xff0c;。 假如沒有mac電腦&#xff0…

Gitee PPM:智能化項目管理如何重塑軟件工廠的未來格局

在數字化轉型浪潮席卷全球的當下&#xff0c;軟件開發行業正經歷著前所未有的變革。隨著企業項目復雜度呈指數級增長&#xff0c;傳統項目管理方式已難以應對多項目并行、跨團隊協作等挑戰。Gitee項目組合管理&#xff08;PPM&#xff09;作為新一代智能化項目管理解決方案&…

node入門:安裝和npm使用

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、安裝npm命令nvm 前言 因為學習vue接觸的&#xff0c;一直以為node是和vue綁定的&#xff0c;還以為vue跑起來必須要node&#xff0c;后續發現并不是。 看…

單例模式,餓漢式,懶漢式,在java和spring中的體現

目錄 餓漢式單例模式 懶漢式單例模式 Spring中的單例模式 關鍵差異對比 在Java和Spring中的應用場景 手寫案例 單例模式是一種創建型設計模式&#xff0c;其核心在于確保一個類僅有一個實例&#xff0c;并提供一個全局訪問點來獲取該實例。下面將詳細介紹餓漢式和懶漢式…

網絡編程——UDP網絡編程

文章目錄 1、sendto()&#xff0c;recvfrom() 與TCP編程不同的是&#xff1a; 無需建立連接&#xff0c;在recvfrom()阻塞等待客戶端的數據&#xff0c;收到數據后進入do something進行數據的處理。 1、sendto()&#xff0c;recvfrom() ssize_t sendto(int socket, void *mes…

OpenSSL詳解

這里寫目錄標題 選項&#xff1a;**通用選項&#xff1a;**1. genrsa&#xff1a;生成RSA密鑰對3. req&#xff1a;生成證書簽名請求4. x509&#xff1a;生成自簽名證書 **證書管理&#xff1a;**1. verify&#xff1a;驗證證書2. x509&#xff1a;查看證書詳情3. crl&#xff…