blender 手柄驱动开发-ubuntu

article/2025/8/12 3:13:28

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 pygameimport 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 problemright_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 pygameimport 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 = Nonedef 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_vectorif 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_vectorelse: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.05return {'PASS_THROUGH'}def execute(self, context):# 初始化Pygamepygame.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_managerself._timer = wm.event_timer_add(0.04, window=context.window)  # 约50FPSwm.modal_handler_add(self)return {'RUNNING_MODAL'}def cancel(self, context):# 清理资源if self._timer:wm = context.window_managerwm.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 pygameimport 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 = Nonedef 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_vectorif 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_vectorelse: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.05return {'PASS_THROUGH'}def execute(self, context):# 初始化Pygamepygame.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_managerself._timer = wm.event_timer_add(0.04, window=context.window)  # 约50FPSwm.modal_handler_add(self)return {'RUNNING_MODAL'}def cancel(self, context):# 清理资源if self._timer:wm = context.window_managerwm.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')


http://www.hkcw.cn/article/LAQuiLGPqe.shtml

相关文章

(9)-Fiddler抓包-Fiddler如何设置捕获Https会话

1.简介 由于近几年来各大网站越来越注重安全性都改成了https协议,不像前十几年前直接是http协议直接裸奔在互联网。接着讲解如何抓取https协议会话。 2.什么是HTTPS? HTTPS就是加过密的HTTP。使用HTTPS后,浏览器客户端和Web服务器传输的数…

差分隐私技术的有效性和局限性

差分隐私(Differential Privacy, DP)由计算机科学家Cynthia Dwork于 2006 年提出,其核心思想是:通过向数据中添加精心设计的随机噪声,确保单个个体的加入或删除不会显著改变数据分析结果的分布,从而从数学上…

篇章七 数据结构——栈和队列

目录 1. 栈(Stack) 1.1 概念 1.图示栈概念: 2.栈在现实生活中的例子: 1.2 栈的使用 1.3 栈的模拟实现 1.接口 2.数组实现 1.4 栈的应用场景 1. 改变元素的序列 2.单链表是否可以实现栈? 2.1 数组实现:顺序栈 2.2 链…

LM393红外避障电路Multisim仿真

电路分析: 开关S1模拟物体的靠近,当按键按下时,表示有物体靠近。 当没有检测到物体时(按键没有按下),LM393D的同相端被R2拉高,电压为5V。 此时反相端的电压经过两个电阻分压后,电压…

C语言进阶--文件操作

1.为什么使用文件? 使用文件可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。 2.什么是文件? 硬盘上的文件都是文件。但是在程序化设计中,我们一般谈到的文件有两种:程序文件、数据文件(从文件功…

力扣刷题Day 66:分割回文串(131)

1.题目描述 2.思路 用了回溯的方法。首先写一个验证字符串是否是回文串的函数,然后遍历s,依次判断从当前字符到下一字符是否是回文串,是的话继续往后走,不是的话往回退。 3.代码(Python3) class Solutio…

【IC】多角多模式信号完整性优化

随着互连效应增强和时钟频率加快,串扰噪声、毛刺和意外信号延迟的发生概率也随之增加,信号完整性 (SI) 问题也日益凸显。由于 65 纳米和 45 纳米设计中横向导线电容的影响日益增大,与 SI 相关的时序违规显著增多。设计必须运行的操作模式和工…

2,QT-Creator工具创建新项目教程

目录 1,创建一个新项目 demo_01.pro(项目配置文件) 类似 CMakeList.txt widget.h(头文件)​ main.cpp(程序入口)​ widget.cpp(源文件)​ widget.ui(界面设计文件)​ 1,创建一个新项目 依次选择: 设置路径: 选择编译器: 如果选择CMake, 就会生成cmakel…

【RocketMQ 生产者和消费者】- 生产者发送同步、异步、单向消息源码分析(1)

文章目录 1. 前言2. send 方法发送同步消息3. sendDefaultImpl 发送消息4. sendKernelImpl 发送同步、异步、单向消息5. sendMessage 发送消息6. 同步发送 sendMessageSync6.1 invokeSyncImpl 同步调用 7. 异步发送 sendMessageAsync7.1 invokeAsyncImpl 异步调用 8. 单向发送 …

【harbor】--配置https

使用自建的 CA 证书来自签署和启用 HTTPS 通信。 (1)生成 CA认证 使用 OpenSSL 生成一个 2048位的私钥这是 自建 CA(证书颁发机构) 的私钥,后续会用它来签发证书。 # 1创建CA认证 cd 到harbor [rootlocalhost harbo…

SOC-ESP32S3部分:23-文件系统

飞书文档https://x509p6c8to.feishu.cn/wiki/SXf5w6seIijVVskvic5cNT2wng4 目前,ESP-IDF 框架支持三种文件系统。 SPIFFS(SPI Flash File System) 简介:SPIFFS 是专门为 SPI NOR Flash 设备设计的轻量级文件系统,适…

[Godot] 如何导出安卓 APK 并在手机上调试

在之前的文章中,我们已经详细介绍了如何配置 Godot 的安卓应用开发环境,包括安装 Android SDK、配置 Java 环境、设置 Godot 的 Android 导出模板等。本篇文章将进一步讲解如何将 Godot 项目导出为安卓 APK 文件,并实现在手机上进行调试运行。…

通用人工智能 (AGI): 定义、挑战与未来展望

摘要 通用人工智能 (AGI) 代表人工智能领域的理想追求,其目标是创造具备人类般广泛智能能力的系统。本文深入探讨 AGI 的核心概念,详细梳理通向 AGI 的潜在技术路径,同时分析实现过程中面临的挑战与应对策略,并对 AGI 的未来发展进…

【CF】Day72——Codeforces Round 890 (Div. 2) CDE1 (二分答案 | 交互 + 分治 | ⭐树上背包)

C. To Become Max 题目: 思路: 二分挺好想的,但是check有点不好写 看到最大值,试试二分,如果 x 可以,那么 x - 1 肯定也可以,所以具有单调性,考虑二分 如何check呢?由于…

Java进阶---JVM

JVM概述 JVM作用: 负责将字节码翻译为机器码,管理运行时内存 JVM整体组成部分: 类加载系统(ClasLoader):负责将硬盘上的字节码文件加载到内存中 运行时数据区(RuntimeData Area):负责存储运行时各种数据 执行引擎(Ex…

Linux《文件系统》

在之前的系统IO当中已经了解了“内存”级别的文件操作,了解了文件描述符、重定向、缓冲区等概念,在了解了这些的知识之后还封装出了我们自己的libc库。接下来在本篇当中将会将视角从内存转向磁盘,研究文件在内存当中是如何进行存储的&#xf…

SRD-12VDC-SL-C 继电器‌接线图解

这个继电器可以使用12伏的直流电源控制250伏和125伏的交流电,也可以控制30伏和28伏的直流电,电流都为10安。 此继电器有5个引脚,各个的作用如下: 引脚4和引脚5为触点, 引脚1和引脚3为线圈引脚,接12伏的直…

Linux下目录递归拷贝的单进程实现

1.实验目的 掌握Linux应用程序命令行参数传递方法掌握POSIX API中文件I/O操作方法,包括:打开文件、关闭文件、创建文件、读写文件、确定和改变文件当前位置 2.实验内容 利用POSIX API在Linux系统上编写应用程序,仿写cp命令的部分功能&#…

哈希:闭散列的开放定址法

我还是曾经的那个少年 1.概念 通过其要存储的值与存储的位置建立映射关系。 如:基数排序也是运用了哈希开放定址法的的思想。 弊端:仅适用于数据集中的情况 2.开放定址法 问题:按照上述哈希的方式,向集合插入数据为44&#xff…

数据库基础

MySQL基础 一、什么是数据库 mysql是数据库服务的客户端 mysql是数据库服务的服务器端 本质:基于C(mysql)S(mysqld)模式的一种服务网络,一套给我们提供数据存取的服务的网络程序 数据库:一…