You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

蓝牙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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 19:30:30