BGE: Turning Arduino Rotation Data In Animation; Blender Stops Responding

Hello,

I am quite new with both python and blender but have decent experience with other programming languages. I’ve been trying to turn the data from a gyroscope chip into a sort of realtime animation but I’ve kinda hit a brick wall.

Here is my python script below:

import sys
sys.path.append("C:\\Users\Student\AppData\Local\Programs\Python\Python35-32\Lib\site-packages")
import time
import serial
serialport = serial.Serial('COM5', 115200)
import bge
cont = bge.logic.getCurrentController()
own = cont.owner
x = serialport.readline()
strx = str(x,'utf-8')
angles = strx.split()
yaw = float(angles[0])
pitch = float(angles[1])
roll = float(angles[2])
print(yaw)
print(roll)
print(pitch)
own.localOrientation = [yaw,pitch,roll]    
serialport.close()   

I am using serial ports to send the data from the arduino and that’s all working perfectly fine. But if I try running this script repeatedly, blender stops responding.

A few things I have tried is running the part where it reads the data and transforms the object in a for loop, but the viewport doesn’t update until the script has completed running. Updating with the viewport with bpy.(whatever goes here).redraw_timer doesn’t fix that either.

As i’ve said, i’m quite new so I don’t know very much, but any help would be appreciated.

Thank you

orientation is a 3x3 matrix.

[xx, xy, xz]
[yx, yy, yz]
[zx, zy, zz]

mathutils has alot of functions to create matrices from angles and such.

I suspect this line to block in case the serial has nothing to read. You could try the following:

print('before read')
x = serialport.readline()
print('after read')

This would be to see when the code is being executed. You could put these print statements at other input/output points to check if it is blocking your script.

Because the BGE runs like this:
render > scripts > render > scripts > etc...

So if a script doesn’t return (one instruction takes a very long time or never finish), it will block the whole BGE mainloop, and the BGE will wait for your script until it finishes, hence not updating the render.


[Edit]: Bingo: https://pythonhosted.org/pyserial/shortintro.html#readline

No timeout means it can hold forever. The timeout is a time limit.

I would recommend you to read carefully the documentation :wink:

Try a refresh methodology

The script is currently creating and destroying the serial port object every frame. I suspect this could be behind the lockup.

So instead of using a python script, use a python module. This allows you to create the serial port once at the start of the game, read from it every frame, and close it when the game ends.

See this stackexchange question:
https://blender.stackexchange.com/questions/80971/blender-game-controller-using-arduino-not-working-properly

Hi Daedalus,

Thank you for your reply. I have looked into using matrices but I decided against it because I have limited knowledge in python so I can’t see how it would be an improvement.

Hi WKnight,

Thank you for your reply. This sounds like this could be the reason for why the animation is not updating. Would you perhaps have any advice on how I could fix this?

Thank you,

Karan Obhrai

Hi rtggg50,

Thank you for your reply as well. Are there any refreshing methodology excluding the methods I have already tried you could recommend please?

Hi sdfgeoff,

Thank you for your reply. I have attempted to fix this, because I do occasionally also get the error when the script running again interferes with a previous script also running as the com port is already being used. But would you have any idea how to access the serial port via modules please because when trying lines of code such as:

from __main__ import *

and

import script

This doesn’t seem to want to run in blender either. Would you know what I may be doing wrong with this. (I did work in a normal blender script but when I tried to run it through the game engine, it could not find the variables for some reason. Perhaps I may need to reference it slightly differently if running it through the game engine e.g. a system file path?

I already told you how to fix it in my message. But it seems like there is an other way if you look at the code used in the link provided by sdfgeoff.

serial.isWaiting() > 0 seems to be a decent test before trying to read anything.

Modules work with the BGE.

I saw you did patch the sys.path, I guess it is because you used pip install pyserial using your Python installation, instead of the one packed with Blender.

To run pip for Blender, you need to run the console from Blender (the “Console” panel), and do something like

import ensurepip
ensurepip.bootstrap()

This will create a pip.py executable somewhere around your_blender_install/Blender/3.5/Scripts.

So you need to use this pip and not the one from your Python installation.


Also, why would you do from __main__ import * ???

I don’t want to make you code complicated. I do not know the specs of your system. However, is a link to how a serial port handles a scene:
https://blender.stackexchange.com/questions/64940/serial-port-close-in-scene-update-post

Another way is to look at your logic blocks and set the Always sensors to do run the check of the code once the serial port close. You could also do a blind code check if it was update with “object.refresh” which is found in webcam game or bge.logic.object.refresh(True):

refresh video

import bge
if hasattr(bge.logic, ‘video’):
bge.logic.video.refresh(True)

I suggested this because if the class is public then it could be assess by a new class under object oriented coding standard which Python (C++ higher language) is.

I notice that you don’t have a way to hold “a pointer” so the module knows when to read the next set of data. If that is the case you need a read methodology that is for very large data sets. However, if you have a really good system with lots of memory and don’t want to run it on a standard store brought device (2 GB or less) then forget it.

Just in case, here is a typical module:

# file: your_project/module.py

# patching pypath
import sys
sys.path.append("C:\\Users\Student\AppData\Local\Programs\Python\Python35-32\Lib\site-packages")

# imports
import weakref
import serial
import bge

# module global
CONNECTION = serial.Serial('COM5', 115200)

# make sure the serial is closed when the BGE is closed too
def finish():
    CONNECTION.close()
weakref.finalize(bge.logic, finish)

# this could be your "main" function
def arduino(controller):
    owner = controller.owner
    
    # your code here...

Then you call the functions you need, such as module.arduino from a Python Module controller.