1
$\begingroup$

I am currently working on a 3D model of an arm. Therefore I get data from 2 sensors with a Arduino and in my Arduino IDE Serial Monitor the data changes as I move the sensor. However, in Blender I only get the first values from the sensors and it does not update as I move the sensors.

enter image description here

Here is my code in Blender:

import threading

import numpy as np
import serial

import bpy

# change this to your MCU COM port, can be viewed in Windows Device Manager
MCU_COM_PORT = "COM3"


# get scene armature object
armature = bpy.data.objects["Armature"]

# get the bones we need
bone_root = armature.pose.bones.get("Root")
bone_upper_arm_R = armature.pose.bones.get("J_Bip_R_UpperArm")
bone_lower_arm_R = armature.pose.bones.get("J_Bip_R_LowerArm")


# this is for syncing motion data between threads
gdata = []

is_stopped = False


def receiveSerial():
    global gdata
    ser = serial.Serial(MCU_COM_PORT)
    
    while not is_stopped:
        c = b""
        buf = b""
        while c != b"\n":
            buf += c
            c = ser.read()
        
        data = buf.decode()
        
        # remove the "println" artifact
        data = data.replace("\r", "")
        
        items = data.split("\t")
        
        # we should be getting 9 entries, two quats, and the last one being empty
        if len(items) != 9:
            print("<SerialHandler>: Incorrect packet received.")
            continue
        
        gdata = items
        
#        print(gdata)
    
    print("<SerialHandler>: Stopped.")
            

# a piece of math code copied from StackOverflow
def multiplyQuaternion(quaternion1, quaternion0):
    w0, x0, y0, z0 = quaternion0
    w1, x1, y1, z1 = quaternion1
    return np.array([-x1 * x0 - y1 * y0 - z1 * z0 + w1 * w0,
                     x1 * w0 + y1 * z0 - z1 * y0 + w1 * x0,
                     -x1 * z0 + y1 * w0 + z1 * x0 + w1 * y0,
                     x1 * y0 - y1 * x0 + z1 * w0 + w1 * z0], dtype=np.float64)
                     

class ModalTimerOperator(bpy.types.Operator):
    # we need these two fields for Blender
    bl_idname = "wm.modal_timer_operator"
    bl_label = "Modal Timer Operator"
    
    _timer = None
    
    def modal(self, context, event):
        global gdata
        
        if event.type == "ESC":
            return self.cancel(context)
    
        if event.type == "TIMER":
            # this will eval true every Timer delay seconds
            items = gdata
            
            print(items)
            
            if not items:
                return  {"PASS_THROUGH"}
            
            w0 = float(items[0])
            x0 = float(items[1])
            y0 = float(items[2])
            z0 = float(items[3])
            w1 = float(items[4])
            x1 = float(items[5])
            y1 = float(items[6])
            z1 = float(items[7])
            
            # convert data to numpy arrays
            q0 = np.array([w0, x0, y0, z0])
            q1 = np.array([w1, x1, y1, z1])
            
            # get inverse transformation of q0, the parent bone
            q0_inv = np.array([w0, -x0, -y0, -z0])
            
            
            # rotate child about parent, to get relative position of the child
            q1_rel = multiplyQuaternion(q0_inv, q1)
            
            # apply transformation
            bone_upper_arm_R.rotation_quaternion[0] = w0
            bone_upper_arm_R.rotation_quaternion[1] = x0
            bone_upper_arm_R.rotation_quaternion[2] = y0
            bone_upper_arm_R.rotation_quaternion[3] = z0
            
            bone_lower_arm_R.rotation_quaternion[0] = q1_rel[0]
            bone_lower_arm_R.rotation_quaternion[1] = q1_rel[1]
            bone_lower_arm_R.rotation_quaternion[2] = q1_rel[2]
            bone_lower_arm_R.rotation_quaternion[3] = q1_rel[3]

            # if refresh rate is too low, uncomment this line to force Blender to render viewport
#            bpy.ops.wm.redraw_timer(type="DRAW_WIN_SWAP", iterations=1)
    
        return {"PASS_THROUGH"}

    def execute(self, context):
        # update rate is 0.01 second
        self._timer = context.window_manager.event_timer_add(0.01, window=context.window)
        context.window_manager.modal_handler_add(self)
        return {"RUNNING_MODAL"}
        
    def cancel(self, context):
        global is_stopped
        is_stopped = True
        context.window_manager.event_timer_remove(self._timer)
        print("<BlenderTimer>: Stopped.")
        return {"CANCELLED"}


if __name__ == "__main__":
    bpy.utils.register_class(ModalTimerOperator)

    # start serial handler    
    t = threading.Thread(target=receiveSerial)    
    t.start()

    # start Blender timer
    bpy.ops.wm.modal_timer_operator()
    
    print("All started.")

Thanks for your help :)

$\endgroup$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.