-T.K.- Lab Notes
  • Home
  • Convention Used
  • STM32
    • Getting Started - STM32 Edition
      • Setting up STM32CubeIDE
      • Going Through A Starter Project
      • Changing STM32CubeIDE Settings
      • Pinout Quick Reference
    • Misc
      • Using Nucleo STLink to Flash Off-board Chips
      • Changing STM32 Default Boot Option
      • STM32 Flash Option Byte Recovery
      • STM32 Systick and Timeout in Interrupt Routines
      • Telesky ST-Link V2 Upgrade Firmware
      • Some Performance Measurements on STM32 MCUs
    • System Core
      • Using GPIO on STM32
      • Setting up External Interrupt on STM32
    • Analog
      • Using ADC on STM32
      • ADC Reading Sequence with DMA on STM32
      • Using OPAMP on STM32
      • Using DAC on STM32
    • Timers
      • Using RTC on STM32
      • Using TIM on STM32
    • Connectivity
      • UART
      • USART
        • USART - CAN Dongle (Fixed Size Serializer with Robust Timeout Handling)
      • CAN
      • FDCAN
      • I2C
      • SPI
        • SPI - GC9A01A LCD Screen
        • SPI - RFID
        • SPI - SD Card
      • Ethernet
        • Ethernet - LWIP
        • Ethernet - UDP
        • Ethernet - UDP Multicast
      • USB - FS
      • USB - HS
    • Middleware
      • FreeRTOS
    • Software Pack
      • STMicroelectronics.X-CUBE-AI - Sine Approximator
  • RISC-V / SoC
    • RISC-V: Baremetal From The Ground Up (Chipyard Edition)
    • Quick Start With Chipyard on Ubuntu or WSL
    • Other Chipyard Stuff
      • Debugging OsciArty with JTAG and command line GDB
      • Debugging BearlyML with JTAG and GDB
      • Booting BearlyML With External SPI Flash
      • Setting Up SD / microSD Card for vcu118 Linux Image
      • More Chipyard Stuff
    • A Minimal Chisel Development Environment with Mill
    • Vivado Stuff
      • Installing Xilinx Vivado on Ubuntu 22.04 / 24.04
      • Arty 35T / 100T UART Pins
      • Configuring Vivado DDR MIG on Arty 35T
      • Configuring Vivado DDR MIG on Nexys Video
      • Vivado Generate Flash Config .mcs File From Bitstream
      • Vivado TCL Scripts
    • Adding Custom Instructions to RISC-V GCC Toolchain
    • Kendryte K230 Bringup
      • K230 EVB Board Resource Overview
    • Intel FPGA Quartus
    • Setting up RISC-V Toolchain on Ubuntu 24.04/22.04
    • Getting Started with Zephyr
      • Getting Start with Zephyr on RISC-V System - Windows
      • Getting Started with Zephyr on RISC-V - Ubuntu
    • C Library Compile Magic
    • Setting up ExecuTorch on Ubuntu 22.04
      • Executorch on ARM
  • Motor Control
    • Recoil FOC Motor Controller
      • 0x00. Theory of Operation
      • 0x01. Components
      • 0x02. Implementation
      • 0x03. Application
    • Recoil Documentation
    • New Controller Board Soldering & Power-on Checklist
    • MJBOTS Moteus setup
    • Failed Attempt on Acceleration- and Velocity-Limited Trajectory Generation
    • Moteus Code Analyze
    • MIT Motor Controller Code Analyze
    • ODrive Setup
    • Setting up Recoil USB-CAN Adapter
      • Setting up Recoil USB-CAN Adapter - Ubuntu
      • Setting up Recoil USB-CAN Adapter - Windows
    • NTC Temperature Sense Resistor Value Calculation
  • ML/RL
    • Setting up NVIDIA Tools
      • Setting up NVIDIA Driver on Ubuntu 22.04 / 20.04
      • Getting Started with NVIDIA Isaac Lab on Ubuntu 22.04 / 24.04
      • Setting up Omniverse on Ubuntu 24.04 (2025 Ver)
      • Creating Custom Training Environment in IsaacLab via Extensions
      • NVIDIA Isaac Gym URDF Import Notes
      • Setting up TensorRT Environment on Ubuntu 22.04 / 20.04
      • Setting up NVIDIA Omniverse Isaac Sim on Ubuntu 22.04 / 20.04
      • Setting up NVIDIA Nsight System and Nsight Compute on Ubuntu 24.04
      • Getting Started with Jetson AGX Orin
        • Getting Started with Jetson Using SDK Manager on Ubuntu 22.04
        • Using Jetson AGX Orin with Provided Ubuntu 20.04 System
        • Setting up Common Software on Jetson AGX Orin
        • Solving USB-CAN and USB CH340 Driver Issue on reComputer Mini J4012
        • [Deprecated] Upgrading Jetson AGX Orin to Ubuntu 22.04
      • Solving Torch Errors
      • [Deprecated] Setting up NVIDIA Isaac Gym on Ubuntu 22.04 / 20.04
    • RL Frameworks
      • Case Study: A Dive Into LeggedGym and RSL-RL Framework
      • Case Study: A Dive Into IsaacLab
      • Getting Started with Mujoco
      • Case Study: A Dive Into Unitree-Mujoco
      • Case Study: Setting up Berkeley Humanoid
      • Case Study: Looking into robot_lab
      • Case Study: Setting up RL-SAR
      • Case Study: Getting Started with LeRobot
      • Case Study: No-Mercy Project
        • Python Mouse and Keyboard Interaction in Game Environment
        • Detecting Phara
      • OpenAI gym + Mujoco Setup
      • Gazebo Setup
    • ROS
      • Setting up ROS on Ubuntu 20.04
      • Setting up ETH ANYbotics/elevation_mapping on Ubuntu 20.04
    • ROS 2
      • Setting up ROS 2 Humble Hawksbill on Ubuntu
      • Setting up ROS 2 Humble Hawksbill on Windows 10
      • ROS 2 Issue in Ubuntu with conda
    • Google Colab
      • Colab Resource Options
      • so-vits-svc 4.0: Colab Flow
    • URDF to MJCF Mujoco Notes
    • OnShape to URDF
    • Audio Stuff
      • Microsoft TTS
      • GPTSoVITS
      • 深入浅出理解 So-VITS-SVC 原理
      • NAI-SVC Experiment Log
      • Setting up ChatTTS on Ubuntu 22.04
    • Setting up AnythingLLM on Ubuntu 22.04
    • Setting up MineDojo Environment
    • Processing the SFU Motion Capture Dataset
    • Torch Profiling
    • Setting up Unitree A1
  • 3D Modeling
    • 3D Print Tolerancing
    • Blender to OnShape Workflow
    • Onshape to Blender Workflow
    • Setting up FBX Plugin for Python on Ubuntu 22.04
    • Install Blender on Ubuntu 22.04
    • Blender Python Related
    • VRoid, MMD, Blender Workflow
  • Tools
    • Windows
      • Install WSL 2
      • Install Make on Windows
      • Remove EFI disk partition
      • SAI Color Flip/Color Inversion
      • Microsoft Visual Studio Create Software Signature
      • Connecting the SIGLENT SDS1104X-U Oscilloscope to Computer
      • Using JADENS Thermal Label Printer
      • Getting Started with XBee (ZigBee)
    • Ubuntu
      • Ubuntu 22.04 Standard Installation Procedure
      • Protobuf
      • Setting up Docker on Ubuntu 22.04
      • Linux Mounting SD Card
      • Partitioning SD card
      • Windows Ubuntu Dual Boot Issues
      • Check Disk / Folder / File Size
      • Test Disk Read/Write Speed
      • Cannot Start Chrome in Ubuntu 22.04 After Changing Network Settings
      • Configure USB Access Permissions (udev rules) on Ubuntu
      • Screen Commands
      • Disabling the "<Application> is not responding." System Message on Ubuntu
      • Install and Configure GlobalProtect UC Berkeley VPN Service on Ubuntu 22.04
      • Solving Gamepad not Detected on Ubuntu 22.04
      • Using 3DConnexion Mouse on Ubuntu with Python
      • Install Cursor the AI Editor on Ubuntu 22.04/24.04
      • Solving the .nfsXXX file cannot be deleted issue
      • Windows Remote Desktop Issues
      • nsswitch.conf
    • Lab Automation
    • Github-Related Info
    • Python
      • Publish Python Package to PyPi
      • Python Logging Utility
      • Python converting bettwen JSON and XML
      • Retrieve Github user avatar with Github API
      • Jupyter Notebook Error
    • Raspberry Pi Setup
    • Clang-Format Style Config
    • CrazyFlie Setting Up
    • Using Oscilloscope: x1 vs x10
    • Using the BWRC 3D Printer
    • Using the Leica Microscope at BWRC
    • Pair XBoxController to Raspberry Pi with Bluetooth
    • Reading FrSky Transmitter SBUS data with STM32
    • Configuring the FrSky TARANIS X9D Plus 2019 RC Controller
    • Applying Notion for Education
    • Gitbook Errata
    • Setting up SteamVR without HMD
    • CMake Best Practices
    • Adobe Premiere Pro Audio Level Settings
  • Mechanical
    • MAD Cycloidal Actuator
    • Dog Stuff
      • Fixing the Unitree A1 Robot Dog Leg Motor
      • Fixing the Unitree A1 Robot Dog Ethernet Port
      • Fixing MIT Mini Cheetah
      • Fixing the Unitree Go1 Robot Dog Ethernet Port
    • 3D Printer Profile
  • Electrical
    • A Note on the Polarity of the Famous TT Motor
    • Wiring Pinmap Convention
    • MCU Pinmap Convention
    • PCB Design and Manufacturing Conventions
    • ESP32 Cam
    • LiPo Safety
    • AS5600 Modification
    • OpenOCD and FTDI Chips
    • FT-LINK FTDI Debugger Design Considerations
    • A Study on Reset Pin Connection
    • Note on CAN Termination Resistor
  • UW
    • Digital-Twin Communication System
    • Unreal Engine Communicate with SteamVR
    • Unreal Engine Socket Communication
    • A Note on Coordinate Systems
    • NewLine Serialization Method
    • Humanoid Design Notes
      • Robot Body Ratio Issue
      • VRM Parameters
      • Note on Face Design and Manufacture
  • Workflow Automation
    • RISC-V Toolbox Website
    • Zigbee-Based Home Automation
      • Setting up Home Assistant on Raspberry Pi to Control Zigbee IoT Devices
      • Update Sonoff Zigbee 3.0 USB Dongle Plus (CC2652P)
  • Finance
    • Finance
    • UC Berkeley Reimbursement
  • Life
    • Some Interview Questions
    • Health Insurance
Powered by GitBook
On this page
  • Pip Install Modules
  • Include external scripts
  • Timed Update

Was this helpful?

  1. 3D Modeling

Blender Python Related

Pip Install Modules

Blender bundled Python is located in this directory: <blender_installation>\<version>\python\bin\python.exe

e.g.

D:\Documents\Blender\3.1\python\bin\python.exe
D:\Documents\Blender\3.1\python\bin\python.exe -m pip install numpy
D:\Documents\Blender\3.1\python\bin\python.exe -m pip install pyserial

Include external scripts

Timed Update

import sys
import os
import json
import logging
import threading

import bpy
import numpy as np
import serial

sys.path.append(os.path.join(bpy.path.abspath("//"), "scripts"))





class UARTTable:
    def __init__(self, port, baudrate=115200, logging_level=logging.INFO):
        self.port = port
        self.baudrate = 115200
        self.ser = None
        self.stop_sig = threading.Event()
        self.stop_sig.clear()

        self.data_table = {}

        self.logger = logging.getLogger(self.__class__.__name__)
        self.logger.setLevel(logging_level)
        
        if not self.logger.handlers:
            ch = logging.StreamHandler()
            ch.setStream(sys.stdout)
            ch.setLevel(logging.DEBUG)
            # ch.setFormatter(logging.Formatter("%(asctime)s %(levelname)s [%(name)s::%(threadName)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"))
            ch.setFormatter(logging.Formatter("%(asctime)s %(levelname)s [%(name)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"))
            self.logger.addHandler(ch)
    
    def connect(self):
        self.logger.info("Connecting to serial port {0}...".format(self.port))
        # close any exising connection
        if self.ser:
            self.ser.close()
        while 1:
            # if main want to exit, we no longer try connect to port
            if self.stop_sig.is_set():
                return
            try:
                self.ser = serial.Serial(self.port, baudrate=self.baudrate)
            except serial.serialutil.SerialException as e:
                self.logger.error(str(e))
                self.logger.debug("Still trying...")
                time.sleep(0.5)
                continue
            
            # we only reach here if serial connection is established
            if self.ser.is_open:
                break

        self.logger.info("Connected.")
    
    def stop(self):
        self.stop_sig.set()
        if self.ser:
            self.ser.close()
        self.logger.info("Stopped.")
    
    def start(self):
        self.logger.debug("Starting...")
        self.recvPacket()
    
    def startThreaded(self):
        self.logger.debug("Starting thread...")
        self.t = threading.Thread(target=self.recvPacket)
        self.t.start()

    def get(self, key):
        return self.data_table.get(key)
    
    def recvPacket(self):
        while 1:
            if self.stop_sig.is_set():
                return
            
            c = b""
            buf = b""
            while c != b"\n":
                if self.stop_sig.is_set():
                    return
                buf += c
                # atomically handle serial object exceptions
                try:
                    c = self.ser.read()
                except (AttributeError, TypeError, serial.serialutil.SerialException) as e:
                    print(e)
                    self.connect()
                    continue

            data = buf.decode()
            # self.logger.debug(data)
            try:
                data = json.loads(data)
            except json.decoder.JSONDecodeError:
                self.logger.warning("packet format error.")
                continue
            key = data.get("key")
            if not key:
                self.logger.warning("packet format error.")
                continue
            
            self.data_table[key] = data.get("value")








MCU_COM_PORT = "COM22"
#FPS = 30        # use 30 if system performance is poor
FPS = 60


# set up logging
logger = logging.getLogger("BPY")
logger.setLevel(logging.DEBUG)

if not logger.handlers:
    ch = logging.StreamHandler()
    ch.setStream(sys.stdout)
    ch.setLevel(logging.DEBUG)
    # ch.setFormatter(logging.Formatter("%(asctime)s %(levelname)s [%(name)s::%(threadName)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"))
    ch.setFormatter(logging.Formatter("%(asctime)s %(levelname)s [%(name)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"))
    logger.addHandler(ch)


# get scene armature object
#track_object = bpy.data.objects["teapot"]
track_object = bpy.data.objects["brush"]

# 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")

uart_table = UARTTable(MCU_COM_PORT, logging_level=logging.DEBUG)

"""
@param bone: the bone object, obtained from armature.pose.bone["<bone_name>"]
@param rotation: rotation in quaternion (w, x, y, z)
"""
def setBoneRotation(bone, rotation):
    w, x, y, z = rotation
    bone.rotation_quaternion[0] = w
    bone.rotation_quaternion[1] = x
    bone.rotation_quaternion[2] = y
    bone.rotation_quaternion[3] = z

"""
a piece of math code copied from StackOverflow
https://stackoverflow.com/questions/39000758/how-to-multiply-two-quaternions-by-python-or-numpy

@param q1: in form (w, x, y, z)
@param q0: @see q1
"""
def multiplyQuaternion(q1, q0):
    w0, x0, y0, z0 = q0
    w1, x1, y1, z1 = q1
    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)
                     
ypr_init = np.zeros((3))
vel_x = 0
vel_y = 0
vel_z = 0


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 ypr_init
        global vel_x
        global vel_y
        global vel_z
        
        if event.type == "ESC":
            logger.info("BlenderTimer received ESC.")
            return self.cancel(context)
        
        if event.type == "R" and event.shift:
            try:
                ypr = uart_table.get("/teapot")[3:6]
                ypr = np.array(ypr)
                
                ypr = ypr / 180 * np.pi
                ypr_init = ypr
            except Exception as e:
                print(e)
            logger.info("Resetting position:")
            return {"PASS_THROUGH"}
            
        if event.type == "TIMER":
            # this will eval true every Timer delay seconds
            data = uart_table.get("/teapot")
            
            
            if not data:
                logger.warning("Invalid joint data")
                return {"PASS_THROUGH"}
            
            # convert data to numpy arrays
            acc = np.array(data[3:6])
            ypr = np.array(data[3:6])
            
            ypr = ypr / 180 * np.pi
            
            ypr -= ypr_init
            
            dt = 1./FPS
            
#            logger.info(ypr)
            vel_x = acc[0] * dt
            vel_y = acc[1] * dt
            vel_z = acc[2] * dt
            
            track_object.location[0] += vel_x * dt
            track_object.location[1] += vel_y * dt
            track_object.location[2] += vel_z * dt
            
            track_object.rotation_euler[0] = -ypr[2]
            track_object.rotation_euler[1] = -ypr[1]
            track_object.rotation_euler[2] = ypr[0]
            
#            logger.debug(str(q0) + str(q1))
#            
#            # get inverse transformation of q0, the parent bone
#            q0_inv = q0 * np.array([1, -1, -1, -1])
#            
#            # rotate child about parent, to get relative position of the child
#            q1_rel = multiplyQuaternion(q0_inv, q1)
#            
#            # apply transformation
#            setBoneRotation(bone_upper_arm_R, q0)
#            setBoneRotation(bone_lower_arm_R, q1_rel)

            # 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(1./FPS, window=context.window)
        context.window_manager.modal_handler_add(self)
        return {"RUNNING_MODAL"}
        
    def cancel(self, context):
        uart_table.stop()
        
        # reset joint position
        track_object.location = (0, 0, 0)
        track_object.rotation_euler = (0, 0, 0)
#        setBoneRotation(bone_upper_arm_R, [1, 0, 0, 0])
#        setBoneRotation(bone_lower_arm_R, [1, 0, 0, 0])
        
        context.window_manager.event_timer_remove(self._timer)
        logger.info("BlenderTimer Stopped.")
        return {"CANCELLED"}


if __name__ == "__main__":
    try:
        logger.info("Starting services.")
        bpy.utils.register_class(ModalTimerOperator)
        
        # uart_table.start()
        uart_table.startThreaded()
        
        # start Blender timer
        bpy.ops.wm.modal_timer_operator()
        
        logger.info("All started.")
    except KeyboardInterrupt:
        uart_table.stop()
        logger.info("Received KeyboardInterrupt, stopped.")

Last updated 10 months ago

Was this helpful?