Arduino 到 Blender 的动作捕捉的代码
2021-01-29 本文已影响0人
T_K_233
整个架构是:
Arduino 读取 4 个电位器的模拟电压值,通过 ADC 采集并转换为浮点数据,通过串口发送给上位机(电脑)
上位机上运行 Blender 中的 Python 代码,读取串口数据并转换,将数据转送给全局的关节数据字典,同时另一个线程中读取字典的值并将关节角度绑定到骨架上
Arduino 程序
/** Blender_Serial_Comm_Arduino_Code.ino
*
* Demonstrates reading four(4) potentiometer sensor readings from a robotics arm
* and sending the data to Blender running Python via the Serial port.
*/
/* uncomment the line below to view floating point data via Serial monitor. */
//#define DEBUG
void setup() {
/* setup the serial port */
Serial.begin(115200);
}
void loop() {
float data[4];
/* read and process the sensor data */
data[0] = analogRead(A0) - 100;
data[1] = analogRead(A1) - 500;
data[2] = analogRead(A2) - 540;
data[3] = analogRead(A3) - 870;
data[0] *= 0.0013;
data[1] *= 0.0013;
data[2] *= 0.0013;
data[3] *= 0.0013;
#ifndef DEBUG
for (int i=0; i<4; i+=1) {
/* convert the 32-bit floating point into 4 8-bit uint8_t buffer array */
uint8_t *buffer;
buffer = (uint8_t *)&data[i];
Serial.write(buffer[0]);
Serial.write(buffer[1]);
Serial.write(buffer[2]);
Serial.write(buffer[3]);
}
#else
Serial.print(data[0]); Serial.print("\t");
Serial.print(data[1]); Serial.print("\t");
Serial.print(data[2]); Serial.print("\t");
Serial.print(data[3]); Serial.print("\t");
#endif
/* this is the end-of-message flag */
Serial.print("\n\n");
delay(20);
}
Python 这边需要安装 pyserial 这个第三方库,而且需要安装到 Blender 自带的 py 环境中
在 Blender 中的 Python 是独立于系统的单独环境,所以需要在这个环境下安装第三方的库。方法如下:
在 Blender 的 Python Terminal 中输入
import sys
sys.exec_prefix
即可得到 Blender 的 Python 的文件位置
image.png打开 Powershell, cd 到那个文件路径下的 bin 文件夹中
cd D:\Documents\Blender\2.82\python\bin
该文件路径下有一个 python.exe,这个就是 Blender 所使用的 Python 文件
运行 pip
.\python.exe -m pip install pyserial
image.png
现在即可在 Blender 中调用这个第三方 serial 库
Blender Python 程序
import math
import struct
from threading import Thread
import bpy
import serial
# define global variables
stop_flag = 0
position_data = []
# get the blender scene objects
armature = bpy.data.objects["Armature"]
bone0 = armature.pose.bones.get("Bone")
bone0.rotation_mode = 'AXIS_ANGLE'
bone1 = armature.pose.bones.get("Bone.001")
bone1.rotation_mode = 'AXIS_ANGLE'
bone2 = armature.pose.bones.get("Bone.002")
bone2.rotation_mode = 'AXIS_ANGLE'
bone2.rotation_axis_angle[1:] = [0, 1, 0]
bone3 = armature.pose.bones.get("Bone.003")
bone3.rotation_mode = 'AXIS_ANGLE'
class ModalTimerOperator(bpy.types.Operator):
bl_idname = "wm.modal_timer_operator"
bl_label = "Modal Timer Operator"
_timer = None
def __init__(self):
pass
def modal(self, context, event):
global stop_flag
if event.type == 'ESC':
stop_flag = 1
# bpy.utils.unregister_class(ModalTimerOperator)
return self.cancel(context)
if event.type == 'TIMER':
if len(position_data) >= 4:
bone0.rotation_axis_angle[0] = (position_data[0] * math.pi)
bone1.rotation_axis_angle[0] = (position_data[1] * math.pi)
bone2.rotation_axis_angle[0] = (position_data[2] * math.pi)
bone3.rotation_axis_angle[0] = -(position_data[3] * math.pi)
return {'PASS_THROUGH'}
def execute(self, context):
self._timer = context.window_manager.event_timer_add(0.1, window=context.window)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def cancel(self, context):
context.window_manager.event_timer_remove(self._timer)
return {'CANCELLED'}
def readData(ser):
c = b""
buffer = b""
while True:
c = ser.read(1)
if c == b"\n":
# detect for end-of-message flag
if ser.read(1) == b"\n":
break
buffer += c
return buffer
def serialCommHandler():
global position_data
# open serial port
ser = serial.Serial(port="COM3", baudrate=115200)
while not stop_flag:
try:
position_data = struct.unpack("<ffff", readData(ser))
except Exception as e:
print(e)
print("Exit condition detected.")
ser.close()
if __name__ == "__main__":
bpy.utils.register_class(ModalTimerOperator)
t = Thread(target=serialCommHandler, args=())
t.start()
# run blender loop
bpy.ops.wm.modal_timer_operator()
另外还有一段单独的 Python 程序用来调试
""" read_serial.py
"""
import struct
import serial
ser = serial.Serial(port="COM3", baudrate=115200)
print(ser.name)
def readData():
c = b""
buffer = b""
while True:
c = ser.read(1)
if c == b"\n":
if ser.read(1) == b"\n":
break
buffer += c
return buffer
while True:
data = struct.unpack("<ffff", readData())
print(data)