Практическая работа - индивидуальное задание / ПР_Индивидуальное_задание_Мягков_БАП2201
.docxМИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ, СВЯЗИ И МАССОВЫХ
КОММУНИКАЦИЙ РОССИЙСКОЙ ФЕДЕРАЦИИ
Ордена Трудового Красного Знамени федеральное государственное бюджетное образовательное учреждение высшего образования
«Московский технический университет связи и информатики»
(МТУСИ)
Кафедра «Интеллектуальные системы в управлении и автоматизации»
(ИСУиА)
ИНДИВИДУАЛЬНОЕ ЗАДАНИЕ
По дисциплине
Технологии промышленного интернета вещей
Выполнили:
Студенты 4-го курса
Группы БАП2201
Ли Самен
Мягков А.К.
Проверил:
к.т.н., доцент
Воронов В.И.
Москва 2026
СОДЕРЖАНИЕ
ВЫПОЛНЕНИЕ 3
ВЫВОДЫ 9
Модуль 2-х осевого джойстика KY-023 представлен на рисунке 1.
Характеристики:
– Рабочее напряжение: 3,3 – 5 В;
– Потребляемый ток: до 0,25 мА;
– Размер: 34х26х32 мм;
– Вес: 11 гр.
Рисунок 1 – Внешний вид и габаритные размеры модуля аналогового
двухосевого джойстика с пятью контактами для подключения
Модуль KY-023 состоит из двух потенциометров на 10 кОм (расположенных перпендикулярно), для определения осей X и Y (рисунок 2) необходимо изменить положение джойстика. Средняя нога каждого потенциометра выведена на разъем J1 (контакты VRX и VRY), а вторая и третья нога подключена к питанию и массе. Дополнительно установлена тактовая кнопка, показания снимаются с разъема J1 (контакт SW), так-же предусмотрено посадочное место для подтягивающего резистора (R1).
Рисунок 2 – Устройство модуля двухосевого джойстика с тактовой кнопкой для нажатия и двумя потенциометрами для отслеживания осей X и Y
Для считывания данных с выводов VRX (ось X) и VRY (ось Y) необходимо использовать аналоговые порт ESP32. (значение от 0 до 5В или от 0 до 1023, как на рисунке 3), а для считывания данных с вывода RW используем цифровой порт (значение 0 В и 5 В или 0 и 1). Так-как один вывод тактовой кнопки подключен к земле и при нажатии на джойстик вниз, происходит замыкание цепи, но возможно ложные срабатывания (наводки). Для получения стабильных показаний, вывод RW необходимо подтянуть к питанию +5В, через подтягивающий резистор R1 или использовать встроенный подтягивающий резистор.
Рисунок 3 – Cхема распределения значений координат X и Y (от 0 до 1023) при перемещении рычага джойстика в крайние положения
ВЫПОЛНЕНИЕ
В отличие от теоретического 10-битного АЦП (0–1023), плата ESP32 обладает 12-битным аналогово-цифровым преобразователем, что расширяет диапазон считываемых значений до 0 – 4095. Логика работы программы (Листинг 1) включает следующие этапы:
1) Непрерывное чтение для сбора данных последовательного порта (COM7, 115200 бод) и парсинг строки, содержащей шесть значений: координаты X, Y и состояние кнопки для каждого из двух модулей KY-023;
2) При запуске программы автоматически вычисляется положение центра стиков путём усреднения выборки показаний за 2 секунды, что компенсирует механическое смещение потенциометров;
3) Нелинейная нормализация – применение мертвой зоны (DEADZONE = 300) для фильтрации шумов и люфта в центральном положении. Значения за пределами мертвой зоны нормализуются в диапазон от -1,0 до 1,0;
4) Использовали экспоненциальное сглаживание как фильтр (коэффициент α = 0,35) для исключения дребезга показаний и обеспечения плавности движения курсора;
5) Преобразование нормализованных значений в 16-битный формат осей Xbox-контроллера (от -32767 до 32767) с последующей инверсией оси Y. Состояния тактовых кнопок (SW) маппируются на виртуальные кнопки «A» и «B».
Листинг 1 – Программа управления двумя стиками с помощью ESP32 на языке python |
import sys import time import serial import vgamepad as vg
PORT = "COM7" # Наш рабочий порт BAUDRATE = 115200
# Последние считанные значения last_values = [2048, 2048, 1, 2048, 2048, 1]
# Центры стиков, будут откалиброваны при запуске center1_x = 2048 center1_y = 2048 center2_x = 2048 center2_y = 2048
# Настройки DEADZONE = 300 SMOOTHING = 0.35 # 0..1, чем больше, тем резче реакция PRINT_DEBUG = True
def clamp(value, low, high): return max(low, min(high, value))
def read_serial_line(ser): global last_values
try: line = ser.readline().decode("utf-8", errors="ignore").strip() if not line: return last_values
parts = line.split(",") if len(parts) != 6: return last_values
values = [int(p) for p in parts] last_values = values return values except Exception: return last_values
def calibrate_center(ser, seconds=2.0): print("Калибровка центра. Не трогай джойстики...") samples = [] start = time.time()
while time.time() - start < seconds: vals = read_serial_line(ser) samples.append(vals) time.sleep(0.01)
if not samples: return 2048, 2048, 2048, 2048
avg_x1 = sum(v[0] for v in samples) // len(samples) avg_y1 = sum(v[1] for v in samples) // len(samples) avg_x2 = sum(v[3] for v in samples) // len(samples) avg_y2 = sum(v[4] for v in samples) // len(samples)
print(f"Центр 1: X={avg_x1}, Y={avg_y1}") print(f"Центр 2: X={avg_x2}, Y={avg_y2}")
return avg_x1, avg_y1, avg_x2, avg_y2
def normalize_axis(raw, center, deadzone): delta = raw - center
if abs(delta) <= deadzone: return 0.0
if delta > 0: denom = 4095 - center - deadzone if denom <= 0: return 0.0 value = (delta - deadzone) / denom return min(value, 1.0) else: denom = center - deadzone if denom <= 0: return 0.0 value = (delta + deadzone) / denom return max(value, -1.0)
def to_xbox_axis(norm_value): return int(clamp(norm_value, -1.0, 1.0) * 32767)
def smooth(prev_value, new_value, alpha): return prev_value * (1.0 - alpha) + new_value * alpha
def main(): global center1_x, center1_y, center2_x, center2_y
try: ser = serial.Serial(PORT, BAUDRATE, timeout=0.05) except Exception as e: print(f"Не удалось открыть порт {PORT}: {e}") sys.exit(1)
gamepad = vg.VX360Gamepad()
time.sleep(2.0) # дать порту стабилизироваться center1_x, center1_y, center2_x, center2_y = calibrate_center(ser, seconds=2.0)
print("Геймпад запущен.") print("SW первого джойстика -> A") print("SW второго джойстика -> B") print("Ctrl+C для выхода")
filtered_lx = 0.0 filtered_ly = 0.0 filtered_rx = 0.0 filtered_ry = 0.0
last_debug_time = 0
try: while True: x1, y1, s1, x2, y2, s2 = read_serial_line(ser)
raw_lx = normalize_axis(x1, center1_x, DEADZONE) raw_ly = normalize_axis(y1, center1_y, DEADZONE) raw_rx = normalize_axis(x2, center2_x, DEADZONE) raw_ry = normalize_axis(y2, center2_y, DEADZONE)
# Сглаживание filtered_lx = smooth(filtered_lx, raw_lx, SMOOTHING) filtered_ly = smooth(filtered_ly, raw_ly, SMOOTHING) filtered_rx = smooth(filtered_rx, raw_rx, SMOOTHING) filtered_ry = smooth(filtered_ry, raw_ry, SMOOTHING)
# Отправка в виртуальный Xbox-геймпад gamepad.left_joystick( x_value=to_xbox_axis(filtered_lx), y_value=to_xbox_axis(-filtered_ly) # инверсия Y )
gamepad.right_joystick( x_value=to_xbox_axis(filtered_rx), y_value=to_xbox_axis(-filtered_ry) # инверсия Y ) # Кнопки if s1 == 0: gamepad.press_button(button=vg.XUSB_BUTTON.XUSB_GAMEPAD_A) else: gamepad.release_button(button=vg.XUSB_BUTTON.XUSB_GAMEPAD_A)
if s2 == 0: gamepad.press_button(button=vg.XUSB_BUTTON.XUSB_GAMEPAD_B) else: gamepad.release_button(button=vg.XUSB_BUTTON.XUSB_GAMEPAD_B)
gamepad.update()
# Отладка раз в 0.2 сек now = time.time() if PRINT_DEBUG and (now - last_debug_time > 0.2): last_debug_time = now print( f"J1 raw=({x1:4d},{y1:4d}) norm=({filtered_lx:+.2f},{filtered_ly:+.2f}) | " f"J2 raw=({x2:4d},{y2:4d}) norm=({filtered_rx:+.2f},{filtered_ry:+.2f})" )
time.sleep(0.01)
except KeyboardInterrupt: print("Остановлено.") finally: ser.close()
if __name__ == "__main__": main() |
На рисунке 4 представлено присоединение двух стиков к плате ESP32.
Рисунок 4 – Присоединение триггеров к ESP32
Для верификации работоспособности системы была проведена проверка с использованием веб-сервиса mitinogame.ru/gamepad-tester. В ходе тестирования инициилизируется виртуальное устройство (xbox 360) в операционной системе. Отклонение модулей KY-023 отображалось перемещением соответствующих осей на экране, а нажатие на стики вызывало фиксацию кнопок «A» и «B».
Рисунок 5 – Фиксация отклонения осей и нажатия виртуальных кнопок
эмулируемого геймпада на веб-сервисе онлайн-тестирования
ВЫВОДЫ
В ходе выполнения работы была разработана программа на языке Python, преобразующая аналоговые сигналы двух модулей KY-023 в данные виртуального Xbox-геймпада. Для обеспечения стабильного управления в коде реализованы процедуры автоматической калибровки центра, программной фильтрации дребезга и настройки мертвой зоны осей. Функциональность созданного устройства успешно подтверждена путем тестирования на специализированном веб-сервисе с фиксацией отклонения стиков и нажатия кнопок.
