美文网首页
Arduino 到 Blender 的动作捕捉的代码

Arduino 到 Blender 的动作捕捉的代码

作者: T_K_233 | 来源:发表于2021-01-29 23:19 被阅读0次

    整个架构是:

    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)
    

    相关文章

      网友评论

          本文标题:Arduino 到 Blender 的动作捕捉的代码

          本文链接:https://www.haomeiwen.com/subject/qwbxzktx.html