强化学习 Reinforcement Learning(五)—— 使用 K20 单片机玩一个 Q 学习小游戏

强化学习 Reinforcement Learning(五)—— 使用 K20 单片机玩一个 Q 学习小游戏

写在前面

因为这个学期需要学习飞思卡尔的K20单片机,突发奇想看看能不能写个Q学习的小游戏让单片机自己玩一下。

本文用到的单片机:KinetisMKDN512

在这里插入图片描述

小游戏内容

在这里插入图片描述
上图中有 5 个房间,分别被标记成房间 0~4,房间外被标记成 5,现在智能体 Agent 被随机丢在 0-4 号 5 个房间中的任意 1 个,它的目标是寻找到离开房间的路。

若智能体可以成功走出房门,则奖励+100。

在这里插入图片描述
整理后用有向图的方式将含有奖励信息的环境画出,如上图所示。

该环境的奖励矩阵如下:

在这里插入图片描述
其中,值为“-1”表示不可移动到的位置,在代码中可以作为判断的依据。如当智能体位于房间 3 时,智能体无法直接到达房间 5,因为房间 3 里没有门可以直接出去。

程序功能

  1. 进行训练并将训练过程的信息显示到数码管上
  2. 训练完成后对模型进行测试,将测试时模型到达目标所用的步数显示到数码管上。

代码部分

主函数代码,包括初始化函数以及Q学习部分。对于训练的过程做了可视化处理,主要是将关键信息显示在数码管上。

/*
 * @Author: ZiSeoi
 * @Date: 2020-12-03 21:51:14
 * @LastEditTime: 2020-12-03 22:09:25
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 */

#include "stdlib.h"
#include "derivative.h" /* include peripheral declarations */

unsigned char num[10]={0xA0,0xBE,0x62,0x2A,0x3C,0x29,0x21,0xBA,0x20,0x28};//1~10
uint32_t Select_LED[6]={0xFFFFEFFF,0xFFFFDFFF,0xFFFFBFFF,0xFFFF7FFF,0xFFFEFFFF,0xFFFDFFFF};//片选

void init(void)//初始化
{
	SIM_SCGC5|=0x200;
	SIM_SCGC5|=0x1000;
	SIM_SCGC5|=SIM_SCGC5_PORTB_MASK;
	SIM_SCGC5|=SIM_SCGC5_PORTE_MASK;
	//使能PORTA时钟,PORTB时钟,PORTD时钟和PORTE时钟
	PORTA_PCR12=0x0100;
	PORTA_PCR13=0x0100;
	PORTA_PCR14=0x0100;
	PORTA_PCR15=0x0100;
	PORTA_PCR16=0x0100;
	PORTA_PCR17=0x0100;
	PORTD_PCR0=0x0100;
	PORTD_PCR1=0x0100;
	PORTD_PCR2=0x0100;
	PORTD_PCR3=0x0100;
	PORTD_PCR4=0x0100;
	PORTD_PCR5=0x0100;
	PORTD_PCR6=0x0100;
	PORTD_PCR7=0x0100;
	GPIOD_PDDR|=0x00FF;    //PD全设置为输出
	GPIOA_PDDR|=0x03F000;  //PA全设置为输出
	GPIOA_PDOR|=0x03F000;  //PA全设置为1
	GPIOD_PDOR=0xFF;

	PORTB_PCR17=0x0A0103;
	PORTB_PCR16=0x0A0103;
	PORTB_PCR11=0x0A0103;
	PORTB_PCR10=0x0A0103;
	NVICICPR2=1<<(88%32);
	NVICISER2=1<<(88%32); //开启PORTB中断
	GPIOB_PDDR&=~0x30C00; //设置4个输入端口
	PORTE_PCR2=0x0A0103;
	PORTE_PCR3=0x0A0103;
	PORTE_PCR4=0x0A0103;
	PORTE_PCR5=0x0A0103;
	NVICICPR2=1<<(91%32);
	NVICISER2=1<<(91%32); //开启PORTE中断
	GPIOE_PDDR&=~0x03C;   //设置4个输入端口
}


//动作数
#define ACTIONS 6
//探索次数
#define episode 100
//目标状态,即:移动到 5 号房间。
#define target_state 5
//γ,折损率,取值是 0 到 1 之间。
#define gamma 0.8

int max(int* p, int m)
{
	int i, max = *p;
	for (i = 1; i < m; i++)
	{
		if (*(p + i) > max)
			max = *(p + i);
	}
	return max;
}

int main()
{
while(1)
{
	init();
	// 经验矩阵。
	int q[6][6] = { 0 };

	int r[6][6] = { 
	{ -1, -1, -1, -1, 0, -1},
	{-1, -1, -1, 0, -1, 100.0},
	{ -1, -1, -1, 0, -1, -1},
	{-1, 0, 0, -1, 0, -1},
	{0, -1, -1, 0, -1, 100.0},
	{ -1, 0, -1, -1, 0, 100.0 } 
	};

	int start_room;
	int current_state;
	int current_action;
	int current_action_point;
	int next_state;
	int step;
	int next_state_max_q;

	// 搜索次数
	int i=0;
	int j=0;
	for (i = 0; i < episode; i++)
	{
		// Agent 的初始位置的状态。
		start_room = rand() % 5;
		// 当前状态。
		current_state = start_room;
		while (current_state != target_state)
		{
			//当前状态中的随机选取下一个可执行的动作。
			current_action = rand() % 6;
			// 执行该动作后的得分。
			current_action_point = r[current_state][current_action];
			if (current_action_point < 0)
			{
				q[current_state][current_action] = current_action_point;
			}
			else
			{
				// 得到下一个状态。
				next_state = current_action;
				// 获得下一个状态中,在自我经验中,也就是 Q 矩阵的最有价值的动作的经验得分。
				next_state_max_q = max(&q[next_state][0],6);
				// 当前动作的经验总得分 = 当前动作得分 + γ X 执行该动作后的下一个状态的最大的经验得分
				// 即:积累经验 = 动作执行后的即时奖励 + 下一状态根据现有学习经验中最有价值的选择 X 折扣率
				q[current_state][current_action] = current_action_point + gamma * next_state_max_q;
				current_state = next_state;
			}
			for(j=1;j>0;j--)//多次显示防止低位数字显示过暗
						{						
									GPIOA_PDOR|=0xFF000;//清除寄存器 
									GPIOD_PDOR=num[current_state];//显示字符
									GPIOA_PDOR&=Select_LED[2];//片选	
									_delay();
									GPIOA_PDOR|=0xFF000;//清除寄存器 
									GPIOD_PDOR=num[next_state];//显示字符
									GPIOA_PDOR&=Select_LED[0];//片选
									delay();
						}
		}
	}

	start_room = rand()%5;
	current_state = start_room;
	step = 0;
	
	// 这里是进行测试,依据训练好的Q值,选择动作。
	while (current_state != target_state)
	{
		// 用两个for循环实现。
		int m = -100;
		int i=0;
		for (i = 0; i < 6; ++i)
		{
			if (m < q[current_state][i])
			{//找最大值
				m = q[current_state][i];
			}
		}
		for (j = 0; j < 6; j++)
		{
			if (m == q[current_state][j])
				next_state = j;
		}
		//Agent 由current_state号房间移动到了next_state号房间
		current_state = next_state;
		step += 1;
	}
	
	for(j=200000;j>0;j--)
				{						
							/*step显示*/
							GPIOA_PDOR|=0xFF000;//清除寄存器 
							GPIOD_PDOR=num[step/100];//显示字符
							GPIOA_PDOR&=Select_LED[5];//片选
							GPIOA_PDOR|=0xFF000;//清除寄存器
							
							GPIOD_PDOR=num[(step-(step%100))/10];//显示字符
							GPIOA_PDOR&=Select_LED[4];//片
							GPIOA_PDOR|=0xFF000;//清除寄存器 
							GPIOD_PDOR=num[step%10];//显示字符
							GPIOA_PDOR&=Select_LED[3];//片选
							
							GPIOA_PDOR|=0xFF000;//清除寄存器 
							GPIOD_PDOR=num[start_room];//显示字符
							GPIOA_PDOR&=Select_LED[1];//片选
				}
}
	return 0;
}

中断函数部分,包括中断向量表

/*
 *    kinetis_sysinit.c - Default init routines for P2
 *                     		Kinetis ARM systems
 */
 
#include "kinetis_sysinit.h"
#include "derivative.h"

/**
 **===========================================================================
 **  External declarations
 **===========================================================================
 */
#if __cplusplus
extern "C" {
#endif
extern uint32_t __vector_table[];
extern unsigned long _estack;
extern void __thumb_startup(void);
#if __cplusplus
}
#endif

/**
 **===========================================================================
 **  Default interrupt handler
 **===========================================================================
 */
void Default_Handler()
{
	__asm("bkpt");
}



/**
 **===========================================================================
 **  Reset handler
 **===========================================================================
 */
void __init_hardware()
{
	SCB_VTOR = (uint32_t)__vector_table; /* Set the interrupt vector table position */
	/*
		Disable the Watchdog because it may reset the core before entering main().
		There are 2 unlock words which shall be provided in sequence before
		accessing the control register.
	*/
	WDOG_UNLOCK = KINETIS_WDOG_UNLOCK_SEQ_1;
	WDOG_UNLOCK = KINETIS_WDOG_UNLOCK_SEQ_2;
	WDOG_STCTRLH = KINETIS_WDOG_DISABLED_CTRL;
}

/* Weak definitions of handlers point to Default_Handler if not implemented */
void NMI_Handler() __attribute__ ((weak, alias("Default_Handler")));
void HardFault_Handler() __attribute__ ((weak, alias("Default_Handler")));
void MemManage_Handler() __attribute__ ((weak, alias("Default_Handler")));
void BusFault_Handler() __attribute__ ((weak, alias("Default_Handler")));
void UsageFault_Handler() __attribute__ ((weak, alias("Default_Handler")));
void SVC_Handler() __attribute__ ((weak, alias("Default_Handler")));
void DebugMonitor_Handler() __attribute__ ((weak, alias("Default_Handler")));
void PendSV_Handler() __attribute__ ((weak, alias("Default_Handler")));
void SysTick_Handler() __attribute__ ((weak, alias("Default_Handler")));


int count=0;
int color=0;
void PORTB_Handler(void)
{
	int temp_color;
	temp_color=LED_change(color);
	if(PORTB_ISFR&0x020000) //SW1
	{	
		GPIOC_PDOR|=0x0FFF;
		if(temp_color==3) GPIOC_PDOR&=~0x07;
		else GPIOC_PDOR&=~(0x01<<temp_color);
	    GPIOC_PDOR|=0x248;
	    count=1;
	}
	else if(PORTB_ISFR&0x010000)//SW2
	{	
		GPIOC_PDOR|=0x0FFF;
		if(temp_color==3) GPIOC_PDOR&=~0x03F;
		else GPIOC_PDOR&=~(0x09<<temp_color);
	    GPIOC_PDOR|=0x240;
	    count=2;
	}
	else if(PORTB_ISFR&0x0800)//SW3
	{	
		GPIOC_PDOR|=0x0FFF;
		if(temp_color==3) GPIOC_PDOR&=~0x1FF;
		else GPIOC_PDOR&=~(0x049<<temp_color);
	    GPIOC_PDOR|=0x200;
	    count=3;
	}
	else if(PORTB_ISFR&0x0400)//SW4
    {	
		GPIOC_PDOR|=0x0FFF;
		if(temp_color==3) GPIOC_PDOR&=~0x0FFF;
		else GPIOC_PDOR&=~(0x249<<temp_color);
		count=4;
    }
	PORTB_PCR17|=0x01000000;
	PORTB_PCR16|=0x01000000;
	PORTB_PCR11|=0x01000000;
	PORTB_PCR10|=0x01000000;
void PORTE_Handler(void)
{
    int temp_GPIOC_PDOR;
    temp_GPIOC_PDOR=LED_change(count);
    if(PORTE_ISFR&0x4)
    {
    	GPIOC_PDOR|=0x0FFF;
    	GPIOC_PDOR&=~temp_GPIOC_PDOR;
    	color=5;
    }
    else if(PORTE_ISFR&0x8) 
    {
    	GPIOC_PDOR|=0x0FFF;
        GPIOC_PDOR&=~(temp_GPIOC_PDOR<<1);
        color=6;
    }
    else if(PORTE_ISFR&0x10)  
    {
    	GPIOC_PDOR|=0x0FFF;
        GPIOC_PDOR&=~(temp_GPIOC_PDOR<<2);
        color=7;
    }
    else if(PORTE_ISFR&0x20)  
    {
    	GPIOC_PDOR|=0x0FFF;
        GPIOC_PDOR&=~((temp_GPIOC_PDOR<<2)|(temp_GPIOC_PDOR<<1)|(temp_GPIOC_PDOR));
        color=8;
    }
    PORTE_PCR2|=0x01000000;
    PORTE_PCR3|=0x01000000;
    PORTE_PCR4|=0x01000000;
    PORTE_PCR5|=0x01000000;
}

/* The Interrupt Vector Table */
void (* const InterruptVector[])() __attribute__ ((section(".vectortable"))) = {
    /* Processor exceptions */
    (void(*)(void)) &_estack,
    __thumb_startup,
    NMI_Handler, 
    HardFault_Handler, 
    MemManage_Handler, 
    BusFault_Handler,
    UsageFault_Handler, 
    0, 
    0, 
    0, 
    0, 
    SVC_Handler, 
    DebugMonitor_Handler, 
    0,
    PendSV_Handler, 
    SysTick_Handler,

    /* Interrupts */
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler, 
    PORTB_Handler,
    Default_Handler,
    Default_Handler,//90
    PORTE_Handler,	
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,
    Default_Handler,	
    Default_Handler,
    Default_Handler,		
};


效果展示

训练时,数码管第四位是当前所处的房间号,第六位是下一次要到达的房间号。
训练完成后,测试结果打出,数码管前三位为测试所用的步数,第五位为初始房间号。

K20单片机Q学习小游戏演示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值