#python #multithreading #timer #modbus
#python #многопоточность #таймер #modbus
Вопрос:
Чего я хочу добиться?
- Считывать данные с сервера Modbus точно каждые 0,05 секунды.
Общая картина заключается в том, что я создаю приложение PyQt5, с помощью которого я хочу сохранить и отобразить данные Modbus. Поэтому позже я смогу использовать их для автоматической настройки PID. Автонастройка PID требует, чтобы данные измерялись с точностью не менее 0,05 секунды. А также точки данных должны быть распределены одинаково следующим образом:
M wait 0.05s then M wait 0.05s then M
и не так:M wait 0.08s then M wait 0.03s then M
(M = measure data)
.
Что я пробовал до сих пор?
- Я попытался реализовать
Threading.Timer
чтение данных каждые 0,05 секунды. - Проблема в том, что точность таймера слишком низкая.
Это код, на котором я тестировал Threading.Timer
точность:
from threading import Timer
import timeit
starttime = timeit.default_timer()
def f():
Timer(0.05, f).start()
global starttime
print("The time difference is :", timeit.default_timer() - starttime)
starttime = timeit.default_timer()
#Here I would read the data
f()
Код выдает этот вывод:
...
The time difference is : 0.07623
The time difference is : 0.07707
The time difference is : 0.07684
The time difference is : 0.07557
...
- Если бы я должен был считывать данные с такой точностью. В долгосрочной перспективе это создало бы огромную разницу во времени.
- В идеале код будет считывать данные, каждый
0.05s
из которых20
считывается в секунду. Но с такой точностью он будет считывать данные в среднем каждый0.07s
и интерпретировать их как0.05s
. - Разница во времени каждую секунду будет
0.07 * 20 - 0.05 * 20 = 0.4 seconds
неприемлемой, потому что через одну минуту временной сдвиг составит24
секунды.
Как я могу повысить точность Threading.Timer
объекта? Или какие другие методы / инструменты я должен использовать?
Другая информация:
Я измерил, сколько времени требуется, чтобы прочитать одно значение с сервера Modbus, используя этот код:
from pyModbusTCP.client import ModbusClient
import timeit
c = ModbusClient("localhost")
c.open()
for i in range(0,10):
starttime = timeit.default_timer()
data_now = c.read_holding_registers(0, 1)
print("Reading 1 register takes:", timeit.default_timer() - starttime)
Вывод:
...
Reading 1 register takes: 0.000297100
Reading 1 register takes: 0.000307600
Reading 1 register takes: 0.000271699
...
Что я использую:
- Windows 10
- Python 3.7 (32-разрядная версия)
Ответ №1:
Вы переоцениваете. Просто сделайте блокировку FPS. Если чтение занимает более 0,05, вам нужно пересчитать в обратном направлении (не выполняется). Если вы измеряете быстро, пересчитайте против желаемых 0,05 с и подождите это время. С помощью этого метода вы можете добиться точных интервалов 0,05 с. Это не может работать, если чтение регистра занимает больше времени, чем ваш период
Это рабочий пример с блокировкой FPS. Ничего особенного. Установите точность, чтобы создать ложную задержку чтения. Установите период для ваших целей (я установил 1 с, вы хотите 0,05 с)
import time
import random
def time_keeper(lost_by_reading,period=1):
if lost_by_reading>period:
pass # a problem because reading takes longer than period
elif lost_by_reading<period:
time.sleep(period-lost_by_reading)
def mock_register_reading(precision=1000):
rand_sleep = random.randint(0,10)/precision
time.sleep(rand_sleep)
_measure = time.time()
period = 1
for i in range(1000):
start = time.time()
mock_register_reading()
## just for log
print('measured after',time.time()-_measure, ', ERROR: ',period-(time.time()-_measure))
_measure = time.time()
### end
finish = time.time()
reading_time = finish-start
time_keeper(reading_time,period = period)
ПРИМЕЧАНИЕ:
Я советую вам не использовать многопоточность в отношении modbus. По моему собственному опыту, modbus и threading не друзья, и, например, чтение регистра через потоки приводит только к катастрофе (на всякий случай, у вас будет идея создавать потоки каждые 0,5 для чтения)
Комментарии:
1. Хорошо, спасибо, постараюсь реализовать. Вернется, когда я узнаю, работает он или нет 🙂
2. Но, как я уже сказал, это всего лишь пример. На вашем месте я бы сохранил значение чтения в паре с объективным временем, чтобы вы всегда могли интерполировать пропущенные значения и всегда калибровать время по объективному времени. Но, может быть, поскольку вы вводите какое-то дифференциальное уравнение, тогда, я думаю, достаточно просто значений шага