Arduino 到 Blender 的动作捕捉的代码

整个架构是:

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 的文件位置

Arduino 到 Blender 的动作捕捉的代码_第1张图片
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("

另外还有一段单独的 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("

你可能感兴趣的:(Arduino 到 Blender 的动作捕捉的代码)