本帖最后由 hmq011212 于 2017-4-3 17:32 编辑
UPDATE: 由于更换电脑的缘故,再加之高中学业繁重(自招考上了),更新会进一步延迟,但是我仍然在继续研究着Mindcub3r,直到帖子完结,然而由于最近占坑太多,并且涉及各种不同的领域,所以时间会比较少,希望大家谅解。
本次更新,删去了乱七八糟的CPP代码,版面更加清爽。
以上。 April 3rd.
另外,删去了Mega网盘的链接,改为大家更为常用的百度网盘(虽然恶心,但是对于大部分人来说,还是比较快的,谁有百度网盘的VIP账号,麻烦分享下,我这里实在是太慢了,请使用PM的形式。):(链接准备中)----------------------------------------------------------------------正文-------------------------------------------------------------------
MindCub3r对于很多玩家来说已经十分熟悉了,当然作为一个新手,也是从搭建错误到不能扫描到成功解魔方过来的,MindCub3r的程序,是一个非常好的研究范本,它不仅仅运用了机械部分的操作,还运用了程序间的通信等高级内容。但是由于采用了Lego方面的编程系统,虽然是开放源代码,但是程序复杂后,就是一个大坑。相信没有人愿意看到这样的程序:
这也已经是高度模块化的程序了。许多人该打退堂鼓了。
作为一个学生,我当然也希望研究一下,于是就催生出了这篇文章。当然毕竟时间有限,我也不可能一天24小时扑在程序上,只有周末才能来研究一下,不可能每天都来更新帖子、给大家回复,所以大致会每周更新一次,统一回复三次。文章会分为几个章节(具体几个章节没想好),每个章节难度不同。当然由于我LDD技术有限,只能使用官方的搭建图,如果哪位能够提供教育版LDD图纸,那就更好了。我们开始看程序:
第一章 机械部分的初始化 有志者,事竟成。 ——引子 一、程序体: (一)、主程序: 1. 闪烁红色指示灯; 2. 调用Banner子程序,显示Banner; 3. 调用Message子程序,显示信息(参数:Reset scan); 4. 调用ScanCal子程序,使扫描臂复位; 5. 调用Message子程序,显示信息(参数:Reset tilt); 6. 调用TiltCal子程序,使翻动臂复位; 7. 终止程序。 (二)、循环体: 1.LpMindCub3r(Mindcuber的循环体) ① 测量反射光线强度(端口2,光线传感器); ② 调用CubeAbsent子程序,检测是否放置魔方; ③ 测量环境光线强度(端口2,光线传感器); ④ 琥珀色指示灯常亮; ⑤ 调用CubePresent子程序,检测现有魔方状态; ⑥ 判断布尔型变量solve是否为真,为真,则执行ScanAndSolve子程序,为假,则执行Scramble子程序; ⑦ 无限循环下去。 2. LpScanCal0(复位扫描臂) ① C电机旋转0.2s,并测量度数,传入变量a; ② 测量度数,传入变量b; ③ 当a大于等于b时,返回真,否则返回假; ④ 执行循环,直至返回值为真。 注:此处事实上使电机旋转到“定位点”,方法是不断判断电机是否停止运动,若停止运动,则说明已经到达“定位点”。如下图: 扫描臂定位点 2. LpScanCal1(复位扫描臂) ① C电机旋转0.2s,并测量度数,传入变量a; ② 测量度数,传入变量b; ③ 当a大于等于b时,返回真,否则返回假; ④ 执行循环,直至返回值为真。
注:此处事实上使电机旋转到“定位点” ,方法是不断判断电机是否停止运动,若停止运动,则说明已经到达“定位点”。如下图: 扫描臂定位点 3. LpTiltCal(复位翻动臂) ① A电机旋转0.2s,并测量度数,传入变量a; ② 测量度数,传入变量b; ③ 当a小于等于b时,返回真,否则返回假; ④ 执行循环,直至返回值为真。 注:此处事实上使电机旋转到“定位点” ,方法是不断判断电机是否停止运动,若停止运动,则说明已经到达“定位点”。如下图标黄色的部分,复位完成后,应当在七孔梁的同侧。 翻动臂定位点
(三)、子程序: 1. Banner(横幅) ① 清屏,用网格状大字在屏幕左上角显示文本“MindCub3r”; ② 在文本“MindCub3r”右上方用像素小字显示文本“v1p9”; ③ 调用BannerVariant子程序,在文本“v1p9”下方用像素小字显示文本“Edu”; ④ 在该行字符下,绘制填充的矩形,长141px,宽3px; ⑤ 在矩形左下,用像素小字显示文本“By David Gilday”; ⑥ 在文本“By David Gilday”左下,用像素小字显示文本“mindcuber.com”; ⑦ 返回主程序。 2. Message【信息(状态?)】 ① 合并文本,参数A指定为调用子程序时所用的参数; ② 在文本“mindcuber.com”下,将合并后的结果用网格状大字显示; ③ 返回主程序; 3. ScanCal(扫描臂计算) ① 将C端口的电机功率设定为40%; ② 执行循环体LpScanCal0,复位电机; ③ C端口中型电机以40%功率倒转100°,结束时制动; ④ 将C端口的电机功率设定为20%; ⑤ 执行循环体LpScanCal1,复位电机; ⑥ 重置电机度数为0°; ⑦ 调用子程序ScanAway,使扫描臂返回工作位置; ⑧ 返回主程序。 4. ScanAway(扫描臂返回工作位置) ① 测量电机旋转度数,传入变量b; 注:由于在LpScanCal1循环体中,已经完成了电机度数的重置,且中途没有退出程序,故此时电机度数为0。 ② 计算a-b的值,变量a为常数-340,并使电机以100%的功率转动(a-b)°; 注:此时(a-b)的值为-340。 ③ 返回主程序。 5. TiltCal(翻动臂计算) ① 将A端口的电机功率设定为-20%(倒转20%); ② 执行子程序TiltOffset,声明变量bit_offest(偏移量)并赋值; ③ 执行循环体LpTiltCal,复位翻动臂; ④ 重置电机度数为0°; ⑤ 调用子程序TiltAway,使翻动臂返回工作位置; ⑥ 返回主程序。 6. TiltAway(翻动臂返回工作位置) ① 测量电机旋转度数,传入变量a; 注:由于在LpTiltCal循环体中,已经完成了电机度数的重置,且中途没有退出程序,故此时电机度数为0。 ② 计算a-b的值,变量b为常数10,并使电机以70%的功率转动(a-b)°; 注:此时(a-b)的值为-10。 ③ 返回主程序。 2016年5月29日 山东省 (注:这一章是我早期完成的,只是改了改日期,实际完成日期为5月21日,若有不妥之处,敬请PM,文章将会持续更新。)
今天是6月4日,我们将要进行小中考,时间比较多,更新频率会大些。每次更新也会多些。有一点做阅卷机的想法,这篇文章完成后,将会写下阅卷机的文章,一样持续更新,这是后话。现在是下午17点25分。开始更新。
第二章 隐秘的部分 重要提示:如果第一章都看不懂了,那么请不要继续看第二章!!! -——引子 这仅仅是开个玩笑,这一部分难度增加了,但是也不是最难的一部分。这一部分你几乎看不见,但是后面还是会用到。所以,这一章还是比较重要的。 首先,我们要引入一些概念,这也是我们第一章结束的原因。这些概念,我都会用最最通俗的语言来叙述。 1. 变量:所谓变量,其实就是“可变的量”,你可以把它想象成一个缸,在这个缸里面放上水,它就叫“水缸”,在这个缸里面放上米,它就叫“米缸”,这个“缸”里放上什么,它里面就有什么,缸里的物质是可以改变的。在编程中,常用来存储可变的量,在程序执行过程中,变量的量是可变的,它存储于内存中。在C++中常用的定义变量的方式为“数据类型 变量名”; 2. 数组(排列):数组也叫排列。所谓数组,其实就是一个变量,只不过这个变量中含有多个元素,怎么理解呢?变量是个缸,缸里放上米,这个缸里每一粒米都是这个缸的“元素”,这个缸就是一个数组,它也存储在内存里。在C++中常用的定义数组的方式为“数据类型 数组名[数组宽度]”,这个“数组宽度”,就是数组的大小; 3. 数据类型:所谓数据类型,实际上就是指这个数据是什么一类东西,比如“abc”是字符型(char)数据[又称“文本型(text)数据”];比如“123”是整数型(int)数据;比如“123.456”是单精度小数型(float)数据;比如“3.1415926535”是双精度小数型(double)数据,以上整数型、单双精度小数型数据,在EV3的编程系统中,统称为“数字型数据”。当然,有一个特例,布尔型(bool)数据,只有“真”与“假”两种状态,被称为“逻辑型数据”。在EV3编程系统中,可用的只有这几种数据类型。 4. 函数:函数不同于数学中的函数,它是由一组命令组成的,你也可以把它视作“模块”,一个模块完成一项任务,函数组合在一起,就是一个程序,在EV3编程系统中,被称为“模块”。 5. 参数:参数与函数有关,就是函数执行时所需要的数据。 (原混乱的代码已删除。) 一、函数 1. InitTables函数(变量表) ① 定义变量dbg,并赋值为0; ② 定义数组imap,并插入元素{0,1,2,3,4,5,0,3,2,1,5,4,2,4,5,3,1,0,5,2,4,1,3,1,0,3,2,5,4,1,2,3,0,4,5,1,4,3,5,0,2,1,5,3,4,2,0,2,1,0,3,5,4,2,3,0,1,4,5,2,4,0,5,1,3,2,5,0,4,3,1,3,0,1,2,4,5,3,2,1,0,5,4,3,4,1,5,2,0,3,5,1,4,0,2,4,0,5,2,1,3,4,1,5,3,2,0,4,2,5,0,3,1,4,3,5,1,0,2,5,0,4,2,3,1,5,1,4,3,0,2,5,2,4,0,1,3,5,3,4,1,2,0}; ③ 定义数组edge0_tab并插入元素{1,7,5,3,43,31,23,35,21,11,19,17}; ④ 定义数组……并插入元素……(我不写了,有需要的自己翻阅上述内容,总之就是定义变量); ⑤ 调用Message函数,显示文本“Find solver”。 ⑦ 执行循环LpInitTables,判断Solver是否响应(此处就是卡在Find solver的循环); ⑧ 播放音调(1000Hz,0.1s,音量100%,播放一次); ⑨ 定义数字型变量pattern,并赋值为0; ⑩ 定义数字型变量pattern_mode,并赋值为0; ⑪ 调用GetPattern函数,返回name, faces, turns, number; ⑫ 调用SetPattern函数,参数faces, turns均使用GetPattern中的返回值; ⑬ 调用CubeDetect函数,检测魔方; ⑭ 返回主程序。 2. △SolverCommand函数(给Solver发信息) ① 删除mc3cmd文件; ② 创建mc3cmd文件,并写入cmd; ③ 关闭mc3cmd文件,返回主程序。 3. △SolverResponse函数(检测Solver是否响应) ① 读取mc3cmd文件,并将其值返回为resp,Mod2后判断是否与1相等,若相等返回done为真,否则返回done为假; ② 关闭mc3cmd文件。 4. GetPattern函数(取解魔方模式函数) ① 判断传入的number值: ⑴ 若为0,则返回name为Solve; ⑵ 若为1,则返回name为CheckerBoard(棋盘形),定义数组checkboard_faces,并添加元素{0,2,1,3,4,5},定义数组checkboard_turns,并添加元素{2,2,2,2,2,2}; (3) 若为2,则返回name为Cube-in-cube(魔方套魔方),定义数组cubes_faces,并添加元素{0,4,2,1,4,0,4,0,4,0,4,5,2,5,1,2,4},定义数组cubes_turns,并添加元素{-1,1,-1,-1,1,2,2,-1,-1,1,2,1,-1,-1,2,2,-1},返回faces为cubes_faces,返回turns为cubes_turns; (4) 若为3,则返回name为Six-spot(蓝点),定义数组spot_faces,并添加元素{4,5,1,3,0,2,4,5},定义数组spot_turns,并添加元素{1,-1,1,-1,1,-1,1,-1},返回faces为spot_faces,返回turns为spot_turns; (5) 若为4,则返回name为snake(S形),定义数组snake_faces,并添加元素{1,5,0,5,3,0,3,5,0,5,1,5,0,1},定义数组snake_turns,并添加元素{-1,-1,1,1,2,1,2,1,-1,1,-1,2,-1,2},返回faces为snake_faces,返回turns为snake_turns; (6) 若为5,则返回name为Superflip(翻棱),定义数组superflip_faces,并添加元素{0,1,4,0,4,2,1,3,0,1,4,5,1,2,3,4,1,2,1,2},定义数组superflip_turns,并添加元素{-1,2,-1,2,2,2,1,-1,-1,2,1,1,-1,2,-1,2,-1,2,-1,-1},返回faces为superflip_faces,返回turns为superflip_turns; (6) 若为-1,则返回name为All(所有),返回faces为空数组,返回turns为空数组; (7) 若为-2,则返回name为Random(随机),返回faces为空数组,返回turns为空数组; (8) 若为-3,则返回name为Scramble(打乱),返回faces为空数组,返回turns为空数组; ② 返回number为5; ③ 返回InitTables函数。 (注:初始状态为-1,即All。吐槽:本来是一个switch语句,结果搞得这么复杂,我想念写两个函数直接return faces,然后再return turns的时代,还有乐高公司麻烦出个全局变/常量和局部变/常量吧,看“*量表”眼花啊,顺便把变量值加进去…… 还有所谓“结构化编程”对于ev3来说就是个笑话,你这是图形化编程,易学倒是易学,对开发大工程来说就是噩梦……) 5. SetPatten函数(设置魔方模式函数) ① 删除文件mc3dat; ② 取数组faces的长度,判断是否介于1-100之间; ⑴ 若介于1-100之间,则向mc3dat中写入数组faces的长度,执行LpSetPattern0循环,继续顺次写入faces、turns数组的各个值; ⑵ 若不介于1-100之间,则向mc3dat中写入0; ③ 关闭mc3dat文件; ④ 执行SolverCommand函数,参数cmd=8; ⑤ 执行LpSetPattern循环,判断解魔方程序是否响应; ⑴ 等待0.1秒; ⑵ 执行SolverResponse函数,检测是否响应; ⑶ 若响应,返回InitTables程序; ⑷ 若无响应,继续执行循环。 6. CubeDetect函数(检测魔方函数) ① 调用1号端口的超声波传感器测量距离; ② 判断6-10厘米处是否有物体,若有物体返回真,否则返回假。
|