蓝牙HID鼠标移动精度问题排查求助
蓝牙HID鼠标移动精度问题排查求助
我最近在开发一个Python任务自动化机器人,通过蓝牙HID协议远程控制鼠标和键盘,底层用C语言写的驱动来发送键鼠指令。现在遇到了鼠标移动精度的问题,试了好几种调整都没解决,来求助各位大佬!
问题现象
- 测试时发送X方向**-50像素**的移动指令,实际光标在屏幕上移动的距离明显远大于50像素
- 尝试实现渐进式移动到目标坐标的功能时,光标经常会越过目标点,没法精准停在想要的位置
技术细节
- C驱动通过Unix Socket接收指令,格式为
mouse_move x y,之后转成蓝牙HID报告发送 - HID鼠标协议使用8位带符号值(范围-128到127)来表示相对移动量
- 蓝牙驱动在LE_TIMER循环中运行,从队列里逐个处理指令
- 我在Python端写了一个计算移动距离、发送渐进移动指令的函数,同时附上C驱动的核心代码
Python端渐进移动函数
def move_mouse_gradually(self, target_x, target_y, from_center=False): start_time = time.time() if from_center: current_x, current_y = 960, 540 else: current_x, current_y = self.get_mouse_position() self.logger.log("MOUSE", f"Iniciando movimento: ({current_x}, {current_y}) → ({target_x}, {target_y})", level="INFO") max_attempts = 150 attempts = 0 min_distance = 10 # pixels move_interval = 0.040 while attempts < max_attempts: current_x, current_y = self.get_mouse_position() dx = target_x - current_x dy = target_y - current_y distance = math.sqrt(dx*dx + dy*dy) if distance <= min_distance: break step_size = 1 if distance > 100: step_size = 8 elif distance > 50: step_size = 4 elif distance > 20: step_size = 2 if dx != 0 or dy != 0: magnitude = math.sqrt(dx*dx + dy*dy) normalized_dx = dx / magnitude normalized_dy = dy / magnitude move_dx = int(normalized_dx * step_size) move_dy = int(normalized_dy * step_size) if abs(dx) > 5 and move_dx == 0: move_dx = 1 if dx > 0 else -1 if abs(dy) > 5 and move_dy == 0: move_dy = 1 if dy > 0 else -1 if abs(dx) <= 5 and random.random() > 0.7: move_dx = 0 if abs(dy) <= 5 and random.random() > 0.7: move_dy = 0 else: move_dx = move_dy = 0 if move_dx == 0 and move_dy == 0: time.sleep(move_interval) attempts += 1 continue self.hid.mouse_move(move_dx, move_dy) actual_interval = move_interval if distance < 30: actual_interval = move_interval * 1.5 # Mais lento perto do alvo time.sleep(actual_interval) attempts += 1 final_x, final_y = self.get_mouse_position() final_distance = math.sqrt((final_x - target_x)**2 + (final_y - target_y)**2) self.update_mouse_position(final_x, final_y) execution_time = (time.time() - start_time) * 1000 self.logger.log_performance("mouse", "move_gradually", execution_time, 0, f"attempts={attempts},final_distance={final_distance:.1f}") return final_distance <= min_distance
C端驱动核心代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include "btlib.h" #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <time.h> #include <stdlib.h> #include <sys/time.h> #include <pthread.h> #include <sys/select.h> #define SOCKET_PATH "/tmp/keymouse-a.sock" #define KEY_DELAY_MIN 66000 #define KEY_DELAY_MAX 132000 #define CLICK_DELAY_MIN 74000 #define CLICK_DELAY_MAX 123000 #define MAX_QUEUE_SIZE 256 #define KEY_RATE_LIMIT 20 #define CLICK_RATE_LIMIT 30 #define MOVE_RATE_LIMIT 10 typedef struct { int type; int arg1; int arg2; } CommandItem; typedef struct { int command_socket; int client_socket; pthread_t receiver_thread; volatile int thread_running; CommandItem key_queue[MAX_QUEUE_SIZE]; int key_queue_head; int key_queue_tail; int key_queue_size; pthread_mutex_t key_mutex; CommandItem click_queue[MAX_QUEUE_SIZE]; int click_queue_head; int click_queue_tail; int click_queue_size; pthread_mutex_t click_mutex; CommandItem move_queue[MAX_QUEUE_SIZE]; int move_queue_head; int move_queue_tail; int move_queue_size; pthread_mutex_t move_mutex; long long last_key_time; long long last_click_time; long long last_move_time; } KeyMouseContext; int lecallback(int clientnode, int op, int cticn); int send_key(int key); int send_mouse(char x, char y, char but); void handle_sigint(int sig); long long get_timestamp(); int random_delay(int min, int max); KeyMouseContext* create_context(); void destroy_context(KeyMouseContext* ctx); volatile int running = 1; KeyMouseContext* g_context = NULL; unsigned char reportmap[99] = {0x05,0x01,0x09,0x06,0xA1,0x01,0x85,0x01,0x05,0x07,0x19,0xE0,0x29,0xE7,0x15,0x00, 0x25,0x01,0x75,0x01,0x95,0x08,0x81,0x02,0x95,0x01,0x75,0x08,0x81,0x01,0x95,0x06, 0x75,0x08,0x15,0x00,0x25,0x65,0x05,0x07,0x19,0x00,0x29,0x65,0x81,0x00,0xC0, 0x05,0x01,0x09,0x02,0xA1,0x01,0x85,0x02,0x09,0x01,0xA1,0x00,0x05,0x09,0x19,0x01, 0x29,0x03,0x15,0x00,0x25,0x01,0x95,0x03,0x75,0x01,0x81,0x02,0x95,0x01,0x75,0x05, 0x81,0x01,0x05,0x01,0x09,0x30,0x09,0x31,0x15,0x81,0x25,0x7F,0x75,0x08,0x95,0x02, 0x81,0x06,0xC0,0xC0}; unsigned char report[8] = {0,0,0,0,0,0,0,0}; unsigned char *name = "ConnectHub"; unsigned char appear[2] = {0xC1,0x03}; unsigned char pnpinfo[7] = {0x02,0x6B,0x1D,0x46,0x02,0x37,0x05}; unsigned char protocolmode[1] = {0x01}; unsigned char hidinfo[4] = {0x01,0x11,0x00,0x02}; KeyMouseContext* create_context() { KeyMouseContext* ctx = malloc(sizeof(KeyMouseContext)); if (!ctx) { printf("[ERROR] Failed to allocate context memory\n"); return NULL; } memset(ctx, 0, sizeof(KeyMouseContext)); ctx->command_socket = -1; ctx->client_socket = -1; ctx->thread_running = 0; if (pthread_mutex_init(&ctx->key_mutex, NULL) != 0 || pthread_mutex_init(&ctx->click_mutex, NULL) != 0 || pthread_mutex_init(&ctx->move_mutex, NULL) != 0) { printf("[ERROR] Failed to initialize mutexes\n"); free(ctx); return NULL; } return ctx; } void destroy_context(KeyMouseContext* ctx) { if (!ctx) return; if (ctx->thread_running) { ctx->thread_running = 0; pthread_join(ctx->receiver_thread, NULL); } if (ctx->client_socket >= 0) { close(ctx->client_socket); } if (ctx->command_socket >= 0) { close(ctx->command_socket); unlink(SOCKET_PATH); } pthread_mutex_destroy(&ctx->key_mutex); pthread_mutex_destroy(&ctx->click_mutex); pthread_mutex_destroy(&ctx->move_mutex); free(ctx); } int enqueue_key_command(KeyMouseContext* ctx, char key) { pthread_mutex_lock(&ctx->key_mutex); if (ctx->key_queue_size >= MAX_QUEUE_SIZE) { pthread_mutex_unlock(&ctx->key_mutex); printf("[WARNING] Key queue full, command dropped\n"); return 0; } ctx->key_queue[ctx->key_queue_tail].type = 1; ctx->key_queue[ctx->key_queue_tail].arg1 = key; ctx->key_queue_tail = (ctx->key_queue_tail + 1) % MAX_QUEUE_SIZE; ctx->key_queue_size++; pthread_mutex_unlock(&ctx->key_mutex); return 1; } int enqueue_click_command(KeyMouseContext* ctx, int button) { pthread_mutex_lock(&ctx->click_mutex); if (ctx->click_queue_size >= MAX_QUEUE_SIZE) { pthread_mutex_unlock(&ctx->click_mutex); printf("[WARNING] Click queue full, command dropped\n"); return 0; } ctx->click_queue[ctx->click_queue_tail].type = 2; ctx->click_queue[ctx->click_queue_tail].arg1 = button; ctx->click_queue_tail = (ctx->click_queue_tail + 1) % MAX_QUEUE_SIZE; ctx->click_queue_size++; pthread_mutex_unlock(&ctx->click_mutex); return 1; } int enqueue_move_command(KeyMouseContext* ctx, int x, int y) { pthread_mutex_lock(&ctx->move_mutex); if (ctx->move_queue_size >= MAX_QUEUE_SIZE) { pthread_mutex_unlock(&ctx->move_mutex); printf("[WARNING] Move queue full, command dropped\n"); return 0; } ctx->move_queue[ctx->move_queue_tail].type = 3; ctx->move_queue[ctx->move_queue_tail].arg1 = x; ctx->move_queue[ctx->move_queue_tail].arg2 = y; ctx->move_queue_tail = (ctx->move_queue_tail + 1) % MAX_QUEUE_SIZE; ctx->move_queue_size++; pthread_mutex_unlock(&ctx->move_mutex); return 1; }
我怀疑可能是Python端的步进计算或者C端的指令队列处理、HID报告转换环节出了问题,但自己排查了好久没找到根因。有没有大佬能帮我分析下,到底是哪一步导致了移动精度的偏差?
备注:内容来源于stack exchange,提问作者Felipe
相关产品推荐
相关产品推荐

