Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Соединяем эллиптический тренажер и pygame

Anna | 15.06.2014 | нет комментариев
Привет, друзья! Случилось некогда так, что для поправления позже травмы я прикупил себе такой вот прибор.

Со своими прямыми обязанностями он справлялся абсолютно удовлетворительно, но было одно «но», и заключалось оно в том, что спидометр путался в показаниях, и следственно, показывал различные итоги по пройденной дистанции. Если идти довольно медлительно, то спидометр вообще молчал. И решено было сделать свой спидометр с… ну вы осознали.

Как объединить тренажер и компьютер

Первое, с чего решено было начать — обнаружить метод получать данные на компьютер. Промежуточным звеном решено было применять плату Arduino.
Отчего Arduino? Потому что под рукой нет ни чего иного подходящего.
При осмотре обнаружилось, что от тренажера к датчику идут два провода.
image
Чего абсолютно хватит, Дабы подключить его к пинам Arduino. Что и было сделано по вот такой вот схеме

На контакт A0 в зависимости от расположения педалей, будет поступать сигнал различной величины.
В ходе экспериментов были перепробованы многие варианты подачи сигнала от микроконтроллера до компьютера, и в результате остановился на таком варианте:
на компьютер безпрерывно подается символ «0», после этого, когда сделан шаг на тренажере, подается«1». Дальнейший шаг — вновь «0» и так по кругу.

Привожу sketch

int pin = A0;
int ledPin = 13;
int minSignal = 600;
bool stateUp = false;
bool lastState = false;
bool oneStep = false;
void setup() {
	pinMode(pin, INPUT);
	pinMode(ledPin, OUTPUT);
	Serial.begin(9600);
}
void loop() {
	int signal = analogRead(pin);
	if (signal > minSignal){
		stateUp = true;
	}
	else{
		stateUp = false;
	}
	if (lastState != stateUp && lastState == false){
	       oneStep = not oneStep;
	}
	else {
	}
	lastState = stateUp;
	Serial.println(oneStep);
	digitalWrite(ledPin, oneStep); //индикатор
}

Игра

Что еще писать на pygame если не игру?

Идея

Эллиптический тренажер это имитация ходьбы на лыжах, следственно это будет гонка лыжников. Всякий шаг, сделанный на тренажере делает персонаж в игре. Сперва хотелось сделать плавное передвижениеускорение персонажа, но в результате, решил отдать предпочтение точности.

Расчеты

Опытным путем было выяснено, что при «оптимальных» обстоятельствах один полный цикл равняется 4-м метрам. Это скорей каждого не сколько проходит человек, а сколько прокручивается центральный диск. Легко примем это значение за аксиому.
На виртуальной трассе 1 метр равняется 1 пикселю. Т.е. всякий шаг перемещаем перснажа на 4 пикселя вперед.
Скорость будет высчитывать всякий шаг.
v = s / t
s = 4 м.
t — время одного шага.
*один шаг — полный цикл педалей.

Энтузиазм

Да, будут графики и спидометр с таймером, но хочется духа соревнования.
А что, если соревноваться будешь не с кем-то а с самим собой, вчерашним? Сказано — сделано.

Сверху персонаж сегодняшний, снизу — вчерашний. Если быть больше точным — персонаж прошлого забега но согласитесь, 1-й вариант звучит резче.

Технические детали

База данных

Безусловно, раз необходимо сберегать информацию о забегах, необходима БД. Я решил применять mysql. Вpython использую библиотеку MySQLdb. В приложении за взаимодействие отвечает класс DataManger.
Схема прилагается.

Пример кода

сlass DataManager:
    def __init__(self):
        self.time = time
        self.currentTimeForLastRace = datetime.now()
        self.currentTime = self.time.time()
        self.speed = 0
        self.db = MySQLdb.connect(host="localhost", user="root", passwd="root", db="skirunner", charset='utf8')
        self.cursor = self.db.cursor()
        self.isGetLastRaceSpeeds = False
        self.dataLastRace = []
        self.lastRaceMinDate = datetime.now()
        self.value = 0
        self.lastValue = 0
        self.impulse = 0
        self.isRaceStart = False
        self.currentRaceId = -1
        self.currentDistanceId = -1
        self.currentProfileId = -1

    def getImpulse(self, value):
        self.impulse = 0

        if self.time.time() - self.currentTime > RESET_SPEED_TIME:
            self.speed = 0
        self.value = value
        if self.value != self.lastValue:
            time = self.time.time() - self.currentTime
            self.impulse = POWER_IMPULSE
            self.isRaceStart = True

            self.speed = STEP / time # метры в секунду
            self.currentTime = self.time.time()
            self.lastValue = self.value

        return  self.impulse

    def getLastRaceDistanceAtCurrentTime(self, raceId,currentTime):
        lastRaceDistance = 0
        dateFormat = "%Y-%m-%d %H:%M:%S.%f"
        if  not self.isGetLastRaceSpeeds:

            sql = """SELECT min(date) FROM runLog WHERE race_id = %s""" % raceId
            self.cursor.execute(sql)
            data = self.cursor.fetchall()
            for rec in data:
                self.lastRaceMinDate = datetime.strptime(rec[0],dateFormat)
            sql = """SELECT distance,date FROM runLog WHERE race_id = %s ORDER BY date DESC""" % raceId
            self.cursor.execute(sql)
            self.dataLastRace = self.cursor.fetchall()
            self.isGetLastRaceSpeeds = True

        if self.isRaceStart:
            time = datetime.now() - datetime.fromtimestamp(currentTime)
            for rec in self.dataLastRace:
                distance, date = rec
                if time <= (datetime.strptime(date,dateFormat) - self.lastRaceMinDate):
                    lastRaceDistance = distance
        return  lastRaceDistance
Графика

Как дозволено увидеть из скриншота выше, графика примитивная, но не няшность это здесь основное. Для ее реализации была использована библиотека pygame. О работе с которой я теснее писал.

Формы


Для форм применял библиотеку PyQt.

Пример кода

class FormProfile(QMainWindow):

    def __init__(self):
        super(QMainWindow, self).__init__()
        uic.loadUi('%s/ui/frm_profile.ui' % DIR, self)
        self.cb_profile_load()
        self.te_newProfile.hide()
        self.bt_addProfile.hide()
        self.bt_cancel.hide()
        self.lb_add.hide()
        self.move(QDesktopWidget().availableGeometry().center() - self.frameGeometry().center())

        self.connect(self.bt_ok, SIGNAL("clicked()"), self.bt_ok_clicked)
        self.connect(self.bt_new, SIGNAL("clicked()"), self.bt_new_clicked)
        self.connect(self.bt_addProfile,SIGNAL("clicked()"), self.bt_addProfile_clicked)
        self.connect(self.bt_cancel, SIGNAL("clicked()"), self.bt_cancel_clicked)
        self.connect(self.bt_graph, SIGNAL("clicked()"), self.bt_graph_clicked)

    def bt_ok_clicked(self):
        self.profileId = self.cb_profile.itemData(self.cb_profile.currentIndex()).toString()
        self.formDistance = FormDistance(self.profileId)
        self.formDistance.show()
        self.hide()

Мне дюже понравился процесс разработки окон. Не труднее, чем в MS studio.
Формы сотворил в приложении Qt 4 Creator.
Импортировал их в код

uic.loadUi('%s/ui/frm_profile.ui' % DIR, self)

Связал события и способы

        self.connect(self.bt_ok, SIGNAL("clicked()"), self.bt_ok_clicked)
        self.connect(self.bt_new, SIGNAL("clicked()"), self.bt_new_clicked)
        self.connect(self.bt_addProfile,SIGNAL("clicked()"), self.bt_addProfile_clicked)
        self.connect(self.bt_cancel, SIGNAL("clicked()"), self.bt_cancel_clicked)
        self.connect(self.bt_graph, SIGNAL("clicked()"), self.bt_graph_clicked)

И отобразил

        self.formProfile = FormProfile()
        self.formProfile.show()
Графики


Для графиков применяется библиотека matplotlib.

Здесь тоже пример кода

import matplotlib.pyplot as plt
    def bt_averageSpeed_clicked(self):
        ...
        plt.plot_date(dates, values,'b')
        plt.plot_date(dates, values,'bo')
        averageSpeed = len(values) > 0 and (lambda: sum(values) / len(values)) or (lambda: 0)
        plt.xlabel(u"Средняя-средняя скорость= %.2f м/с либо %.2f км/ч" % (float(averageSpeed()),float(averageSpeed()) / 1000 * 3600))
        plt.ylabel(u"Средняя скорость (м/с)")
        plt.title(u"График скоростей профиля %s" % dm.getProfileNameById(self.profileId))
        plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%d/%m/%y'))
        plt.gcf().autofmt_xdate()
        plt.grid(True)
        plt.show()

Хотелось бы подметить, что для отображения кириллицы, необходимо подключить поддерживающие шрифты.

from matplotlib import rc
   font = {'family': 'Droid Sans',
        'weight': 'normal',
        'size': 14}
        rc('font', **font)
Чтение данных с arduino

Для этой цели применял библиотеку serial.
Дальнейший код запускается в отдельном потоке.

def getDataFromSimulator():
    global valueFromSimulator, isRunnig
    ser = serial.Serial('/dev/ttyACM0', 9600)
    while isRunnig:
       value =  ser.readline()
       try:
           valueFromSimulator = int(value)
       except:
           pass

Переменная valueFromSimulator в ином потоке применяется только для считывания.
Запуск 2-х потоков.

t1 = threading.Thread(target=main,args = (self.profileId,self.distanceId))
t2 = threading.Thread(target=getDataFromSimulator)
t2.start()
t1.start()

Видеодемонстрация плохого качества

Как и заказывали.

Буду рад примечаниям, критике и предложениям.
Все исходники здесь

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB