blender 手柄驱动开发-ubuntu

复制代码
ubuntu 如何安装blender
官网blender.org下载tar.xz压缩文件
tar -xvf xxx.tar.xz

如何启动blender,命令行输入:
blender

如何在blender中安装pygame模块
需要找到blender中的python解释器路径

import sys
print(sys.executable)

然后在终端terminal中使用以下命令
$ "xxxx/python" -m pip install pygame

在ubuntu系统下,使用blender执行脚本时,看不到日志信息。可以使用logging 模块调试程序。

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

logger.debug("详细调试信息")
logger.info("常规信息")
logger.warning("警告信息")
logger.error("错误信息")
这时候打印的信息,在控制台显示

ubuntu 使用blender如果出现卡死,可以使用如下命令,强制关闭blender 界面
pkill -9 blender

# 安装测试工具
sudo apt install jstest-gtk joystick

# 检测手柄
jstest /dev/input/js0

终端会出现如图所示的结果,最下面一行是 摇杆和开关的状态。如果不清楚映射关系,可以操控手柄,观察数值变化。

根据测试,遥控手柄的键位映射关系如下

摇杆Axes :

左横向:Axes 4

左纵向:Axes 1

右横向:Axes 3

右纵向:Axes 0

Axes 2 未知。手柄上没有控件对应Axes 2.

按钮 buttons:

SWA: 0

SWB:2

SWC:3

手柄上左上角的旋钮未知。

也可以通过 jstest-gtk图形工具,测试手柄的键位映射。

选择 properties.

晃动摇杆,或者切换SWA/B/C 开关,即可观测数值变化。

通过Axes 和 按钮 buttons, 即可完成后续的开发工作。

以下是关于游戏手柄的操作说明。

1.确保游戏手柄通过usb接口连接到ubuntu 系统;

2.确保blender 安装了pygame 模块,并可以正常使用;

pygame可以自动识别游戏手柄。

手柄摇杆映射参考:

复制代码
left_stick_x = joystick.get_axis(4)  # 左摇杆 X 轴,横轴
left_stick_y = joystick.get_axis(1)  # 左摇杆 Y 轴, 纵轴
right_stick_x = joystick.get_axis(3)  # 右摇杆 X 轴,横轴
right_stick_y = joystick.get_axis(0)  # 右摇杆 Y 轴,纵轴

开发日志

执行脚本的时候,容易 发生闪退。

频繁发生以下现象

以下代码可以测试摇杆输出值,但是会引发blender is not responding 问题。

复制代码
import bpy
import math
import random
import time
import mathutils
import pygame

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

logger.debug("详细调试信息")
logger.info("常规信息")
logger.warning("警告信息")
logger.error("错误信息")


import pygame

# 初始化 Pygame
pygame.init()
pygame.joystick.init()

# 检测手柄数量
joystick_count = pygame.joystick.get_count()
if joystick_count == 0:
    print("未检测到手柄!")
    exit()

# 初始化第一个手柄
joystick = pygame.joystick.Joystick(0)
joystick.init()
print(f"手柄名称: {joystick.get_name()}")
print(f"摇杆数量: {joystick.get_numaxes() // 2}")  # 每个摇杆占 2 个轴(X/Y)

# 主循环
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 获取摇杆状态(不依赖事件,直接读取)
    left_stick_y = joystick.get_axis(1)  # 左摇杆 X 轴(通常 -1 左,1 右)
    
    right_stick_x = joystick.get_axis(3) 
    
    left_stick_x = joystick.get_axis(4) # have some problem
    
    right_stick_y = joystick.get_axis(0)
    

    # 打印摇杆值(保留 2 位小数)
    print(f"左摇杆: X={left_stick_x:.2f}, Y={left_stick_y:.2f} | 右摇杆: X={right_stick_x:.2f}, Y={right_stick_y:.2f}", end="\r")

    pygame.time.delay(50)  # 降低输出频率

pygame.quit()

以下是测试程序和功能说明

复制代码
import bpy
import math
import random
import time
import mathutils
import pygame

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

logger.debug("详细调试信息")
logger.info("常规信息")
logger.warning("警告信息")
logger.error("错误信息")



class GamepadModalOperator(bpy.types.Operator):
    bl_idname = "object.gamepad_modal_operator"
    bl_label = "Gamepad Modal Control"
    
    _timer = None
    _joystick = None
    
    def modal(self, context, event):
        # 首先处理Blender事件(如ESC键退出)
        if event.type in {'ESC'}:
            return self.cancel(context)
        
        # 只在TIMER事件中处理手柄输入,避免过于频繁的更新
        if event.type == 'TIMER':
            # 处理Pygame事件队列
            pygame.event.pump()
            
            # 获取所有Pygame事件但不处理(避免阻塞)
            for py_event in pygame.event.get():
                # 处理按钮事件
                if py_event.type == pygame.JOYBUTTONDOWN:
                    print(f"按钮 {py_event.button} 按下")
                    if py_event.button == 0:  # A按钮示例
                        print("back")
                        
                        # 指定物体的名称
                        object_name = "drone"  # 替换为你的物体名称
                        obj = bpy.data.objects.get(object_name)
                        
                        if obj:
                            # 定义移动向量(局部坐标系)
                            move_vector = mathutils.Vector((0, 0, -0.2))  # 沿着局部坐标系的 X 轴移动 1 个单位
                            # (x,z,y)
                            # 将局部坐标系的向量转换为世界坐标系
                            world_move_vector = obj.matrix_world @ move_vector - obj.matrix_world.translation
             
                            # 更新物体的位置
                            obj.location += world_move_vector
                        
                
                    if py_event.button == 3:  # A按钮示例
                        print("forward")           
 
                        object_name = "drone" 
                        obj = bpy.data.objects.get(object_name)
            
                        if obj:
                            # 定义移动向量(局部坐标系)
                            move_vector = mathutils.Vector((0, 0, 0.2))  # 沿着局部坐标系的 X 轴移动 1 个单位
                            # (x,z,y)
                            # 将局部坐标系的向量转换为世界坐标系
                            world_move_vector = obj.matrix_world @ move_vector - obj.matrix_world.translation
             
                            # 更新物体的位置
                            obj.location += world_move_vector
                        else:
                            print(f"物体 '{object_name}' 未找到!")  
 
             
                if py_event.type == pygame.JOYBUTTONUP:
                    print(f"按钮 {py_event.button} 释放")
            
            
                # 方向键(hat)事件
                if event.type == pygame.JOYHATMOTION:
                    print(f"方向键 {event.hat} 值: {event.value}")
                    if event.value== (-1,0):
                        print("left turn!!!")
                        
                    if event.value== (1,0):
                        print("right turn!!!")
                    
                    if event.value== (0,1):
                        print("up turn!!!")
         
                    if event.value== (0,-1):
                        print("down turn!!!")



            # 直接读取轴状态(更高效的方式)
#            if self._joystick:
#                # 获取左摇杆状态(轴0和1)
#                axis_x = self._joystick.get_axis(0)
#                axis_y = self._joystick.get_axis(1)
#                
#                # 应用死区过滤
#                deadzone = 0.2
#                move_speed = 0.1
#                
#                if context.active_object:
#                    if abs(axis_x) > deadzone:
#                        context.active_object.location.x += axis_x * move_speed
#                    if abs(axis_y) > deadzone:
#                        context.active_object.location.y -= axis_y * move_speed  # Y轴反转
#                    
#                    # 获取右摇杆状态(轴2和3)
#                    axis_rx = self._joystick.get_axis(2)
#                    axis_ry = self._joystick.get_axis(3)
#                    
#                    if abs(axis_rx) > deadzone:
#                        context.active_object.rotation_euler.z -= axis_rx * 0.05
#                    if abs(axis_ry) > deadzone:
#                        context.active_object.rotation_euler.x += axis_ry * 0.05
        
        return {'PASS_THROUGH'}
    
    def execute(self, context):
        # 初始化Pygame
        pygame.init()
        pygame.joystick.init()
        
        # 检查手柄连接
        if pygame.joystick.get_count() == 0:
            self.report({'ERROR'}, "未检测到手柄设备")
            return {'CANCELLED'}
        
        # 初始化第一个手柄
        self._joystick = pygame.joystick.Joystick(0)
        self._joystick.init()
        
        print(f"已连接手柄: {self._joystick.get_name()}")
        
        # 设置定时器,控制更新频率
        wm = context.window_manager
        self._timer = wm.event_timer_add(0.04, window=context.window)  # 约50FPS
        wm.modal_handler_add(self)
        
        return {'RUNNING_MODAL'}
    
    def cancel(self, context):
        # 清理资源
        if self._timer:
            wm = context.window_manager
            wm.event_timer_remove(self._timer)
        
        if hasattr(self, '_joystick') and self._joystick:
            self._joystick.quit()
        
        pygame.quit()
        print("手柄控制已退出")
        return {'CANCELLED'}
 
def register():
    bpy.utils.register_class(GamepadModalOperator)
 
def unregister():
    bpy.utils.unregister_class(GamepadModalOperator)
 
# 测试运行
if __name__ == "__main__":
    register()
    bpy.ops.object.gamepad_modal_operator('INVOKE_DEFAULT')

以下脚本可以正常输出。

复制代码
import bpy
import math
import random
import time
import mathutils
import pygame

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

logger.debug("详细调试信息")
logger.info("常规信息")
logger.warning("警告信息")
logger.error("错误信息")



class GamepadModalOperator(bpy.types.Operator):
    bl_idname = "object.gamepad_modal_operator"
    bl_label = "Gamepad Modal Control"
    
    _timer = None
    _joystick = None
    
    def modal(self, context, event):
        # 首先处理Blender事件(如ESC键退出)
        if event.type in {'ESC'}:
            return self.cancel(context)
        
        # 只在TIMER事件中处理手柄输入,避免过于频繁的更新
        if event.type == 'TIMER':
            # 处理Pygame事件队列
            pygame.event.pump()
            
            # 获取所有Pygame事件但不处理(避免阻塞)
            for py_event in pygame.event.get():
                # 处理按钮事件
                if py_event.type == pygame.JOYBUTTONDOWN:
                    print(f"按钮 {py_event.button} 按下")
                    if py_event.button == 0:  # A按钮示例
                        print("back")
                        
                        # 指定物体的名称
                        object_name = "drone"  # 替换为你的物体名称
                        obj = bpy.data.objects.get(object_name)
                        
                        if obj:
                            # 定义移动向量(局部坐标系)
                            move_vector = mathutils.Vector((0, 0, -0.2))  # 沿着局部坐标系的 X 轴移动 1 个单位
                            # (x,z,y)
                            # 将局部坐标系的向量转换为世界坐标系
                            world_move_vector = obj.matrix_world @ move_vector - obj.matrix_world.translation
             
                            # 更新物体的位置
                            obj.location += world_move_vector
                        
                
                    if py_event.button == 3:  # Y按钮示例
                        print("forward")           
 
                        object_name = "drone" 
                        obj = bpy.data.objects.get(object_name)
            
                        if obj:
                            # 定义移动向量(局部坐标系)
                            move_vector = mathutils.Vector((0, 0, 0.2))  # 沿着局部坐标系的 X 轴移动 1 个单位
                            # (x,z,y)
                            # 将局部坐标系的向量转换为世界坐标系
                            world_move_vector = obj.matrix_world @ move_vector - obj.matrix_world.translation
             
                            # 更新物体的位置
                            obj.location += world_move_vector
                        else:
                            print(f"物体 '{object_name}' 未找到!")  
 
             
                if py_event.type == pygame.JOYBUTTONUP:
                    print(f"按钮 {py_event.button} 释放")
            
            
                # 方向键(hat)事件
                if event.type == pygame.JOYHATMOTION:
                    print(f"方向键 {event.hat} 值: {event.value}")
                    if event.value== (-1,0):
                        print("left turn!!!")
                        
                    if event.value== (1,0):
                        print("right turn!!!")
                    
                    if event.value== (0,1):
                        print("up turn!!!")
         
                    if event.value== (0,-1):
                        print("down turn!!!")



            # 直接读取轴状态(更高效的方式)
#            if self._joystick:
#                # 获取左摇杆状态(轴0和1)
#                axis_x = self._joystick.get_axis(0)
#                axis_y = self._joystick.get_axis(1)
#                
#                # 应用死区过滤
#                deadzone = 0.2
#                move_speed = 0.1
#                
#                if context.active_object:
#                    if abs(axis_x) > deadzone:
#                        context.active_object.location.x += axis_x * move_speed
#                    if abs(axis_y) > deadzone:
#                        context.active_object.location.y -= axis_y * move_speed  # Y轴反转
#                    
#                    # 获取右摇杆状态(轴2和3)
#                    axis_rx = self._joystick.get_axis(2)
#                    axis_ry = self._joystick.get_axis(3)
#                    
#                    if abs(axis_rx) > deadzone:
#                        context.active_object.rotation_euler.z -= axis_rx * 0.05
#                    if abs(axis_ry) > deadzone:
#                        context.active_object.rotation_euler.x += axis_ry * 0.05
        
        return {'PASS_THROUGH'}
    
    def execute(self, context):
        # 初始化Pygame
        pygame.init()
        pygame.joystick.init()
        
        # 检查手柄连接
        if pygame.joystick.get_count() == 0:
            self.report({'ERROR'}, "未检测到手柄设备")
            return {'CANCELLED'}
        
        # 初始化第一个手柄
        self._joystick = pygame.joystick.Joystick(0)
        self._joystick.init()
        
        print(f"已连接手柄: {self._joystick.get_name()}")
        
        # 设置定时器,控制更新频率
        wm = context.window_manager
        self._timer = wm.event_timer_add(0.04, window=context.window)  # 约50FPS
        wm.modal_handler_add(self)
        
        return {'RUNNING_MODAL'}
    
    def cancel(self, context):
        # 清理资源
        if self._timer:
            wm = context.window_manager
            wm.event_timer_remove(self._timer)
        
        if hasattr(self, '_joystick') and self._joystick:
            self._joystick.quit()
        
        pygame.quit()
        print("手柄控制已退出")
        return {'CANCELLED'}
 
def register():
    bpy.utils.register_class(GamepadModalOperator)
 
def unregister():
    bpy.utils.unregister_class(GamepadModalOperator)
 
# 测试运行
if __name__ == "__main__":
    register()
    bpy.ops.object.gamepad_modal_operator('INVOKE_DEFAULT')
相关推荐
敢敢变成了憨憨1 小时前
java操作服务器文件(把解析过的文件迁移到历史文件夹地下)
java·服务器·python
敲键盘的小夜猫1 小时前
Milvus向量Search查询综合案例实战(下)
数据库·python·milvus
简简单单做算法2 小时前
基于mediapipe深度学习的虚拟画板系统python源码
人工智能·python·深度学习·mediapipe·虚拟画板
愿望会实现吧4 小时前
|从零开始的Pyside2界面编程|绘图、布局及页面切换
python
zstar-_4 小时前
【Ragflow】24.Ragflow-plus开发日志:增加分词逻辑,修复关键词检索失效问题
人工智能·python·llm
love530love4 小时前
【笔记】2025 年 Windows 系统下 abu 量化交易库部署与适配指南
大数据·运维·人工智能·windows·笔记·python·conda
love530love4 小时前
【笔记】为 Python 项目安装图像处理与科学计算依赖(MINGW64 环境)
开发语言·图像处理·人工智能·windows·笔记·python·numpy
奉系坤阀4 小时前
Ubuntu终端性能监视工具
linux·运维·服务器·python·ubuntu
YYXZZ。。4 小时前
PyTorch-Transforms的使用(二)
人工智能·pytorch·python
lczdyx4 小时前
一键净化Excel数据:高性能Python脚本实现多核并行清理
python·excel·pandas·数据清洗·数据处理·自动化办公·openpyxl