ロボットの倒立振子の検討の記録
以下の検討の前で行った検討内容は、このリンク先で紹介しています。
モータの切り替え点を変動させて制御してみる。その3
前の検討では、傾き振動の頂点の傾き(下記傾きの矢印)を、各山や谷の頂点を調べて判断してみました。
(山の頂点の傾きが減ったら後進中と判断、 谷の頂点の傾きが減ったら前進中と判断)

もっと簡単に後進中と判断、 谷の頂点の傾きが減ったら前進中する方法として、各谷の頂点の変動に着目して、
この値が下がっる変化であればに後進中、上がった変化であればに前進中と判断するコードが次ぎのコードです。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# TCPサーバープログラム(/usr/local/apps/raspiAPumeTest.py)
# [Raspberry Pi 3 Model A+]と[UMEHOSHI ITA]を乗せたモータ付き台車のサービスから呼び出される
# 倒立振子を試すコード
import subprocess
from collections import deque
import os # ファイル有無を調べるため追加 ★
def exists(path):
try:
os.stat(path)
return True
except (OSError, AttributeError):
# ファイルが存在しない、またはos.statが使えない場合にFalseを返す
return False
# 前か後かの移動中を判断を、波の勾配(微分値)を使って判断すし、目標角の補正値を取得するクラス
from collections import deque
class Detector:
def __init__(self, size: int = 6):
self.dq_former = deque()# 前半の値を保持する変数(データはこちらからを入れる。注意:最新情報はこちら)
self.dq_latter = deque()# 後半の値を保持する変数(注意:キューなのでこちらが時間的に前になる)
self.size = size
# 前半と後半の合計値を保持する変数
self.sum_former = 0.0 # 「差分更新(スライディング・ウィンドウ)」という手法を使う
self.sum_latter = 0.0
self.prev_valley = 0 # 前の谷の値
self.rtnVal = 0
self.call_count = 0
def append(self, data: float) -> float:
self.dq_former.append(data) # 新しいデータを追加
self.sum_former += data
if len(self.dq_former) <= self.size:
return 0,0
# 以下は前半のキューが一杯になった後の追加処理
removed = self.dq_former.popleft() # 前半」の先頭の追い出し
self.sum_former -= removed
self.dq_latter.append(removed) # データを後半に移動
self.sum_latter += data
# 後半キューが一杯になるまでの処理
if len(self.dq_latter) <= self.size:
return 0,0
# 以下は前半と後半が一杯になった。
removed = self.dq_latter.popleft() # 前半」の先頭の追い出し
self.sum_latter -= removed
#
tilt_former = self.dq_former[-1]-self.dq_former[0] # 最近の傾き
tilt_latter = self.dq_latter[-1]-self.dq_latter[0] # 直前の傾き
self.call_count += 1
peak_flag = tilt_latter > 0 and tilt_former < 0 # 山の頂点
valley_flag = tilt_latter < 0 and tilt_former > 0 # 谷の頂点
if not( peak_flag or valley_flag ) or self.call_count < 10:
return self.rtnVal,tilt_former # 変曲点でない
#
dif_sum = abs(self.sum_former) - abs(self.sum_latter) # # 変曲点の前と後の傾きの差を取得
if abs(self.prev_valley-data) > 20: # 差が大きすぎる場合は雑音とsて無視
return self.rtnVal,tilt_former
#
if peak_flag : # -------------------上に山の変曲点
if False: pass
# elif abs(tilt_former) < 1.5 :# 傾きが小さくなった
# self.rtnVal = -2.01
# elif abs(tilt_former) < 1.5 + 0.5:
# self.rtnVal = -1.01
else:
self.rtnVal = -0.01
elif valley_flag : # -------------------下に谷の変曲点]
if self.prev_valley != 0 and data>self.prev_valley+1: # 変曲点が以前が上がっている?
self.rtnVal = -2.01
elif self.prev_valley != 0 and data<self.prev_valley-1: # 変曲点が以前が下がっている?
self.rtnVal = 2.01
# elif abs(tilt_former) < 1.8:
# self.rtnVal = 2.51
# elif abs(tilt_former) < 1.8 + 0.9:
# self.rtnVal = 1.51
else:
self.rtnVal = 0.01
self.prev_valley = data # 谷の頂点を記憶
self.call_count = 0
#
return self.rtnVal,tilt_former
#
def sum_tilt_former(self):
return self.dq_former[-1]-self.dq_former[0] # 最近の傾き
# ---- LED用の出力とタクトスイッチ用入力 のためのGPIO初期化-----
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.OUT) # GPIO21を出力に設定
GPIO.output(21, GPIO.HIGH) # ON(3.3V)
for no in [6, 16, 17]:
GPIO.setup(no, GPIO.IN, pull_up_down=GPIO.PUD_UP) # プルアップ付き入力
#------ GPIO初期化 終了 -------------------------------------
import signal # システムが閉じることを判断するため追加 ★
import board
import busio
from adafruit_ssd1306 import SSD1306_I2C # SSD1306ディスプレイ用
import adafruit_vl53l1x # VL53L1X使用 レーザー測距センサーモジュール用
# オープンソースハードウェアの設計・製造・販売を行うアメリカの企業のAdafruit(エイダフルート)モジュール利用
from PIL import Image, ImageDraw, ImageFont
import time
i2c = busio.I2C(board.SCL, board.SDA)# --- I2C初期化 ---
# --- SSD1306ディスプレイ初期化 (128x64の場合) -----------
oled = SSD1306_I2C(128, 64, i2c)
oled.contrast(128) # 0?255
oled.fill(0) # --- クリア
oled.show() # ---表示
# --- Pillowで描画領域を作成 ---
image = Image.new("1", (oled.width, oled.height))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()# --- フォント設定 ---
def draw_text(txt: str, row=0, showFlag = True, newImageFlag = False, font=font, fill=255):
global image,draw
if newImageFlag:
image = Image.new("1", (oled.width, oled.height)) # イメージ作り直し(全体クリア)
draw = ImageDraw.Draw(image)
# --- テキスト描画 (0=黒、255=白)上記設定で、横21文字---
draw.text((0, row*15), txt , font=font, fill=255)
# --- 画面に表示 ---
oled.image(image)
if showFlag: oled.show()
draw_text(f"UMEHOSHI ITA",0)
# 9軸センサー BNO055 制御 ---------------------------------------------------
import smbus # I2C通信をPythonから簡単に扱うためのモジュール
import os
# BNO055 の初期化
BNO055_ADDRESS = 0x28 # BNO055のI2Cアドレス(ADRピンがGNDなら0x28、VDDなら0x29になります)
BNO055_OPR_MODE = 0x3D # 動作モードを設定するためのレジスタ
BNO055_EULER_H_LSB = 0x1A # オイラー角(方位・ロール・ピッチ)のデータが始まるアドレス
bus = smbus.SMBus(1)# 引数の1でRaspberry PiのボードGPIO2: SDA、GPIO3: SCLを指定
bno055_calib_bin_path="/usr/local/apps/bno055_calib.bin" # キャリブレーションデータファイルパス
# このキャリブレーションデータファイルが存在しなければ、作成する。
try:
os.stat(bno055_calib_bin_path) # ファイルが存在しない場合は、エラー
except OSError:
bus.write_byte_data(BNO055_ADDRESS, BNO055_OPR_MODE, 0x00) # 設定変更(OPR_MODE)でCONFIGモードに切り替える
time.sleep(0.05)
# センサーをリセット(0x3FのSYS_TRIGGERレジスタのビット7をセット)
bus.write_byte_data(BNO055_ADDRESS,0x3F, 0x20)
time.sleep(0.7) # リセット後は再起動まで時間がかかる
# 出力単位(UNIT_SEL)を設定(0x00で「角度=度(°)」単位)
bus.write_byte_data(BNO055_ADDRESS,0x3B, 0x00)
# センサーフュージョンを有効にするNDOFモードに変更
bus.write_byte_data(BNO055_ADDRESS,BNO055_OPR_MODE, 0x0C) # NDOFモードへ
time.sleep(0.05)
while True: # NDOFモードで全てのセンサー(SYS, GYR, ACC, MAG)が 3 になるまで動かす。
cal = bus.read_byte_data(BNO055_ADDRESS, 0x35)
sys = (cal >> 6) & 0x03
gyr = (cal >> 4) & 0x03
acc = (cal >> 2) & 0x03
mag = (cal >> 0) & 0x03
draw_text(f"bno055 NDOF Calibrating...",0, showFlag = False,newImageFlag = True) # キャリブレーションの開始
draw_text(f"SYS:{sys}, GYR:{gyr}, ACC:{acc}, MAG:{mag}", 1)
time.sleep(0.5)
if sys == 3 and gyr == 3 and acc == 3 and mag == 3 : break # キャリブレーション完了?
# 各値が 3 になれば完全キャリブレーション完了です
#
calib_data = bus.read_i2c_block_data(BNO055_ADDRESS, 0x55, 22)
with open(bno055_calib_bin_path, "wb") as f:
f.write(bytearray(calib_data)) # オフセット値を読み出して保存
#
draw_text(f"End Calibration",2) # キャリブレーションデータファイル作成終了
# オフセット調整(センサーの取り付け位置や方向を自動補正)
bno055_offset_path="/usr/local/apps/bno055_offset.txt"
# 上記ファイル内に X軸が北を向く時にHeading、水平に置いた時にRollと Pitchが記憶される
make_offset_mode=False # "bno055_offset.txt"オフセット調整ファイル作成モード
try:
with open(bno055_offset_path, "r") as fr:
s = fr.readline() # 一行読み取り(改行を含めて)
draw_text(f"bno055 offset setting",0,newImageFlag = True)
a = s.split(",")
heading_offset = float(a[0]) # オフセット調整値取得
roll_offset = float(a[1])
pitch_offset = float(a[2])
print(f"offset value heading:{heading_offset}, roll:{roll_offset}, pitch:{pitch_offset}")
except:
make_offset_mode = True # bno055_offset.txtのオフセット調整作成モード
bus.write_byte_data(BNO055_ADDRESS, BNO055_OPR_MODE, 0x00) # 設定変更(OPR_MODE)でCONFIGモードに切り替える
time.sleep(0.05)
with open(bno055_calib_bin_path, "rb") as f:
calib_data = list(f.read(22)) # 別途bno055_calib_write.pyで行ったキャリブレーションの記憶ファイルを読む
# 設定モードへ
bus.write_byte_data(BNO055_ADDRESS, 0x3D, 0x00)
time.sleep(0.025)
# キャリブレーションデータを書き込んで、調整情報を復元
bus.write_i2c_block_data(BNO055_ADDRESS, 0x55, calib_data)
# NDOFモードに戻す
bus.write_byte_data(BNO055_ADDRESS, 0x3D, 0x0C)
time.sleep(0.05)
# 出力単位(UNIT_SEL)を設定(0x00で「角度=度(°)」単位)
bus.write_byte_data(BNO055_ADDRESS,0x3B, 0x00)
def to_signed(val):
"""16ビット値を符号付き整数に変換"""
if val >= 0x8000:
val -= 0x10000
return val
prev1_Pitch = 0 # 前のPitchの測定値
prev2_Pitch = 0 # 前のPitchの測定値
prev3_Pitch = 0 # 前のPitchの測定値
def read_euler():
''' make_offset_modeがTrueの場合は、現在の
heading:左右への向き, roll:左右の傾き, pitch:上下の傾きを返す。
make_offset_modeがFalseの場合は、調整過程のheading, roll, pitchを返す'''
#
global prev1_Pitch,prev2_Pitch,prev3_Pitch # 前のPitchの測定値
data = bus.read_i2c_block_data(BNO055_ADDRESS, BNO055_EULER_H_LSB, 6)
# 各要素が 1 バイト(0〜255)の整数を6個のリストで得られる。(1 LSB = 1/16 度)
# データはリトルエンディアン形式(下位→上位の順)
heading = (data[1] << 8) | data[0] # 方位角(北基準のYAW)
roll = (data[3] << 8) | data[2] # ロール角(左右の傾き)
pitch = (data[5] << 8) | data[4] # ピッチ角(前後の傾き)
# ロールとピッチは符号付き
roll = to_signed(roll) # 16ビット値を符号付き整数に変換
pitch = to_signed(pitch) # 16ビット値を符号付き整数に変換
# スケーリング(1 LSB = 1/16 度)
heading = heading / 16.0 #
roll = roll / 16.0
pitch = pitch / 16.0
if make_offset_mode == False: #オフセット調整処理
# X軸が北を向く時にHeadingが0、水平に置いた時にRollと Pitchが0になるオフセット調整
heading = heading - heading_offset # headingズレ補正
heading = heading if heading >= 0 else heading + 360
roll = roll - roll_offset # rollズレ補正
if roll > 90:
roll = -(90 - roll)
elif roll < -90:
roll = -(-90 - roll)
pitch = pitch - pitch_offset # pitchズレ補正
if pitch > 180:
pitch = -(360 - pitch)
elif pitch < -180:
pitch = -(-360 - pitch)
#
prev3_Pitch = prev2_Pitch # 前の測定値記憶
prev2_Pitch = prev1_Pitch # 前の測定値記憶
prev1_Pitch = pitch # 前の測定値記憶
return heading, roll, pitch # 左右への向き, 左右の傾き, 上下の傾きを返す
if make_offset_mode: #オフセット調整処理
count = 100 # ロボットを水平にしてはX軸が北を向いてから安定に必要な予想回数
while True:
h, r, p = read_euler()
print(f"Heading: {h:7.2f}, Roll: {r:7.2f}, Pitch: {p:7.2f}")
count -= 1
print(f" {count}が0になるまでに、ロボットを水平にしてはX軸が北を向くように置いてください。")
draw_text(f"Before CountReaches 0",0,showFlag = False, newImageFlag = True)
draw_text(f"Place it horizontally",1,showFlag = False)
draw_text(f" and point it north",2,showFlag = False)
draw_text(f" count:{count}",3)
time.sleep(0.2)
if count <= 0:
with open(bno055_offset_path, "w") as fw:
fw.write(f"{h},{r},{p}\n") # オフセット調整データ書き込み
heading_offset, roll_offset,pitch_offset=h,r,p
break # 上記でX軸が北を向く時にHeading、水平に置いた時にRollと Pitchを記憶
# --------------------------------------------------------------------------------
vl53 = adafruit_vl53l1x.VL53L1X(i2c)# VL53L1X使用 レーザー測距センサーモジュール初期化
print("VL53L1X Start measuring...")
vl53.start_ranging()
distance = vl53.distance
time.sleep(0.5)
print(f"Distance: {distance} mm")
# umetcp umeusb 通信関連----------------------------------------------------
import os
import umetcp
from umetcp import send_message, receiveData
import socket
import umeusb
import traceback
sock = None # クライアントと通信するソケット
def my_tcp_receive_file_func(filepath):
''' 受信したumehoshiアプリ用「.umh」のファイルより、
「UME専用Hexコマンド」の文字列をusbへ出力する'''
name,ext = os.path.splitext(filepath)
if not ext == ".umh" : return
with open( filepath ) as f: ss=f.readlines()
ss="".join(ss[1:])
print(ss)
umeusb.send_cmd(ss, quantity=0) # TCPで受信した 「.umh」データを[UMEHOSHI ITA]へ送る
umetcp.tcp_receive_file_func = my_tcp_receive_file_func # tcpファイル受信データの処理を置き換え
def my_tcp_recieve_message(msg):
if msg.startswith("G:"):
file_path = msg[2:]
if exists(file_path):
umetcp.send_file(sock, file_path)
else:send_message(sock, f"G:{file_path}Not Operation\n")
else: umeusb.send_cmd(msg)
umetcp.tcp_receive_message_func=my_tcp_recieve_message # TCO受信文字列(UME専用Hexコマンド)処理を置き換え
def my_usb_receive_func(bin):
'usb受信のイベントで実行するデフォルト処理'
global sock
#print("-----------",bin)
ss = bin.decode('utf-8')
a=ss.split('\n')
for s in a:
s = s.strip()
if s == "": continue
if sock != None:
print(f"USB receive:{s}")
send_message(sock, s) # [UMEHOSHI ITA]からの応答メッセージをTCPで返す
else: print( f"USB receive:{s}" )
umeusb.usb_receive_func = my_usb_receive_func # USB受信データの処理を置き換える。
umeusb.init_sub()
import threading
t_id = threading.Thread(target=umeusb.read_loop)
t_id.start()
#ip="192.168.4.1"
ip=umetcp.get_wlan0_ip() # IPアドレス取得
while ip == None:
ip=umetcp.get_wlan0_ip()
if ip : break
time.sleep(0.1)
#umeusb.send_cmdfile("/usr/local/apps/uStartInit.umh") # ロボット初期化("R009D020010004E")
umeusb.send_cmdfile("/usr/local/apps/pwm_pi3ma_init.c.umh") # ロボット初期化
server_addr =(umetcp.get_wlan0_ip(), 59154)
hostname=socket.gethostname()
print("Serverの情報:",hostname, server_addr)
def info_show2( pitch , target_angle):
''' IPアドレスpとポート番号の情報と、ピッチ角度、目標角度をSSD1306ディスプレイに表示する'''
draw_text(f"{server_addr[0]},{server_addr[1]}",0,showFlag = False, newImageFlag = True)
draw_text(f"Pitch:{pitch:5.1f} {target_angle}",2,showFlag = True)
def save_results(results, last_str): # 結果保存用
with open('/usr/local/apps/log2.txt', 'w') as fw:
for t in results:
fw.write(f'{t[0]}:{t[1]},pitch:{t[2]},periodo:{t[3]},tilt:{t[4]},ajv:{t[5]}\n')
fw.write(last_str)
# # ピッチなどの計測と、その制御ループ----------------------------------------------------------
measure_loop_flag=True # ピッチなどの計測と、その制御ループを続けるためのフラグ
def measure_loop():
global measure_loop_flag # 測定監視ループフラグ
next_measure_time=0 # 測定間隔制御用(次の測定の時間を記憶する)
flag_push = False # 倒立制御開始ボタンが押されてから倒立制御を終えるまでTrue
flag_control = False # 倒立制御中である間だけTrue
start_control_time=0 # 制御開始の時間(秒)
start_forward_time = 0.05 # 制御開始前のモータ前進を行う期間(秒)
target_angle=23.5 # 目標角度 理想予測値
# target_angle = 22.4 # 実験用
# target_angle = 22.5 # 実験用
# target_angle = 23.0 # 実験用
target_angle = 23.2 # 実験用
time_now=0 # 現在の測定時間
motor_ctrl_time=0 # motor制御用コマンドをSUB送信した時の時間を記憶
results = [] # 制御履歴を残すリスト(制御終了時にファイル化)
detector=None # 変更点判定
ajv=0 # target_angleに対する調整値
pwmS=""
pwmS_bak=""
pwdN=0 # PWM デューティ比の大きさ(0〜6)
pwdN_bak=6 # 上記の設定前の状態
pitch_bak=0
umeusb.usb_send("R0080005600005B", quantity=0) # 最大値setPWM6の(0x0BFFF)
#
while measure_loop_flag:
if GPIO.input(6) == GPIO.LOW: # SW2スイッチ(緑)が押された? リブート処理------------
measure_loop_flag=False
draw_text(f"Rebooting.",0,showFlag = True, newImageFlag = True)
time.sleep(0.01)
try: # shell=False(デフォルト)で呼び出すのが安全
sock.close()
subprocess.run(["sudo", "reboot"], check=True)
except subprocess.CalledProcessError as e:
draw_text(f"Reboot fail.",0,showFlag = True, newImageFlag = True)
break
#
if GPIO.input(17) == GPIO.LOW: # SW4スイッチ(黒色)が押された? 制御終了-----------
flag_push = False
flag_control = False
save_results(results, last_str="End SW\n") # 結果を保存
#
if flag_push==False and GPIO.input(16) == GPIO.LOW: # 制御開始用SW3スイッチ(黄色)が押された?
time.sleep(2)
results = []
detector=Detector(size=6) # 判定器
ajv=0 # target_angleに対する調整値
pwmS_bak=''
umeusb.usb_send("R0080005600005B", quantity=0) # 最大値:setPWM6の(0x0BFFF)
pwdN_bak=6
# umeusb.usb_send("R0080005500005C", quantity=0) # setPWM5の(0x07FFF)
# pwdN_bak=5
start_control_time = time.time() # 制御スタート時間
flag_push = True
umeusb.usb_send("R009D020200004D", quantity=0) # Forward 最初の助走
#
time_now=time.time()
if next_measure_time > time_now:
time.sleep(next_measure_time-time_now)
continue # 次の周期まで待つ
next_measure_time = time_now + 0.002 # 次の測定時間を更新
#
startM=time.time()
# for ck in range(3): # # ジャイロ測定処理(雑音対策の繰り返し)
# heading, roll, pitch = read_euler() # ーーージャイロ測定ーーー
# if pitch < 150: break # 適正と予測される値?
heading, roll, pitch = read_euler() # ーーージャイロ測定ーーー
if pitch > 150: pitch = pitch_bak
else: pitch_bak=pitch
endM=time.time()
if flag_push == False : info_show2(pitch,target_angle) # 測定値の表示
#
if flag_push and flag_control == False: # 倒立制御に入るまでの処理
if time.time() > start_control_time + start_forward_time:
umeusb.usb_send("R009D020300004C", quantity=0) # Back 最初の倒立のための逆転
if pitch > target_angle+ajv-6.5: # 倒立角に達した?
flag_control = True
#
if flag_control :# 倒立振子制御中
ajv,tilt_former=detector.append(pitch) # 変曲点を求めるためのデータ記憶
#ajv=0
pwdN=0
if False: pass
elif pitch < target_angle+ajv-6.5 or pitch > target_angle+ajv+6.5:
pwdN=6
if pwdN!=pwdN_bak:
umeusb.usb_send("R0080005600005B", quantity=0) # 最大値:setPWM6の(0x0BFFF)
elif pitch < target_angle+ajv-1.5 or pitch > target_angle+ajv+1.5:
pwdN=5
if pwdN!=pwdN_bak: # USB送信量を抑える工夫
umeusb.usb_send("R0080005500005C", quantity=0) # setPWM5の(0x07FFF)
elif pitch < target_angle+ajv-0.7 or pitch > target_angle+ajv+0.7:
pwdN=4
if pwdN!=pwdN_bak: # USB送信量を抑える工夫
umeusb.usb_send("R0080005400005D", quantity=0) # setPWM4の(0x4FFF)
elif pitch < target_angle+ajv-0.5 or pitch > target_angle+ajv+0.5:
pwdN=3
if pwdN!=pwdN_bak: # USB送信量を抑える工夫
umeusb.usb_send("R0080005300005E", quantity=0) # setPWM3の(0x38FF)
elif pitch < target_angle+ajv-0.3 or pitch > pitch < target_angle+ajv+0.3:
pwdN=2
if pwdN!=pwdN_bak: # USB送信量を抑える工夫
umeusb.usb_send("R0080005200005F", quantity=0) # setPWM2の(0x1FFF)
elif pitch < target_angle+ajv-0.2 or pitch > target_angle+ajv+0.2:
pwdN=1
if pwdN!=pwdN_bak: # USB送信量を抑える工夫
umeusb.usb_send("R00800051000060", quantity=0) # 最小:setPWM1の(0x0FFF)
pwdN_bak = pwdN
# モータの停止、逆転、正転
if pwdN == 0: # motor停止
pwmS=f'S{pwdN}'
if startM-motor_ctrl_time>0.01 or pwmS != pwmS_bak: # USB送信量を抑える工夫
umeusb.usb_send("R009D020A00003E", quantity=0) # STOP
pwmS_bak = pwmS
motor_ctrl_time = startM
#
elif pitch <= target_angle+ajv: # 逆転
pwmS=f'B{pwdN}'
if startM-motor_ctrl_time>0.01 or pwmS != pwmS_bak: # USB送信量を抑える工夫
umeusb.usb_send("R009D020300004C", quantity=0) # Back
pwmS_bak = pwmS
motor_ctrl_time = startM # モータ制御コマンドを送った時間をメモ
#
else: # (pitch > target_angle+ajv): #正転
pwmS=f'F{pwdN}'
if startM-motor_ctrl_time>0.01 or pwmS != pwmS_bak: # USB送信量を抑える工夫
umeusb.usb_send("R009D020200004D", quantity=0) # Forward
pwmS_bak = pwmS
motor_ctrl_time = startM
#
#
results.append( ( f'{pwmS}', startM, pitch, endM-startM, tilt_former, ajv ) ) # ファイル化情報
umeusb.usb.flush()
if pitch < 5 or pitch > 70 or startM > start_control_time + 10: #この時間で制御を終了
umeusb.usb_send("R009D020A00003E", quantity=0) # STOP
flag_push = False
flag_control = False
save_results(results, last_str=f"End pitch:{pitch} time:{startM-start_control_time}.\n") # 結果を保存
#
#
#
#info_show() # ディスプレイへ表示
t_id2 = threading.Thread(target=measure_loop)
t_id2.start()
# 終了時に実行したい処理 ★
def handle_shutdown(signum, frame):
global measure_loop_flag
if measure_loop_flag == False: return
print(f"シャットダウンを検知しました (Signal: {signum})")
measure_loop_flag = False
draw_text(f"Wait....",0,showFlag = True, newImageFlag = True)
time.sleep(10)
draw_text(f"Shutdown.",0,showFlag = True, newImageFlag = True)
time.sleep(0.01)
# SIGTERM(システム終了時の標準信号)を登録 ★
signal.signal(signal.SIGTERM, handle_shutdown) # ★
# TCPサーバー起動処理
try:
serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversock.bind(server_addr) # IPとポート番号を指定します
print(f"{server_addr}:接続要求を待つ")
serversock.listen()
except : serversock = None
while serversock != None:
print("接続を待って、接続してきたら許可")
sock, address = serversock.accept()#サーバの接続受け入れ
print("接続相手:",address)
try:
receiveData( sock ) # 受信ループ
except Exception as e:
print(f"例外の種類: {type(e)}")
print(f"スタックトレース: {traceback.format_exc()}")
draw_text(f"wait next",0,showFlag = True, newImageFlag = True)
sock.close()
またそれ以外にも、PWMの切り替えタイミングを変更しています。
残念ながら、大きな好転は得られませんでした。(コードが少なく、同じ効果のように感じる)