写在前面

从暑假初至8月28号晚上比赛结果出来,第七届起重机创意大赛终于圆满结束,经历暑假45天的努力(实际摸鱼30天,爆肝15天),我们以三箱1分33秒的成绩拿到了国二第一位,然而与前一位比赛用时差了13秒,想短时间内突破基本不可能,因此也能接受这个结果。前些天突发感想,想写一篇文章来记录这次比赛的技术思路,以交流学习。比赛视频我也会上传至b站。
合影

当然也要感谢412工作室各位大佬的帮助,一起通宵调车的日子很是怀念。

一、任务说明

这里直接贴部分比赛通知源文。

“第七届物流技术(起重机)创意赛”的主题为:物料搬运机器人。设计、制作一台物料搬运机器人(以下简称作品),通过自主有序的控制方式将物品从取物区按通行规则搬运到堆码区。

比赛场地如图,图来自比赛通知。可大致总结为,设计一种全自动机械结构,从起始点出发,将放在取物区的三个物一次搬运到堆物区,并按要求码放,搬运时必须穿过障碍A与障碍B的连线,一次一箱,需要往返三次。该机构应具有起升和抓取装置,且作为自由独立体,能全自动运行,可以通过识别功能自动规划路径,具备独立电池电源,允许采用结构型轨道作为行走导向。

第七届起重机大赛比赛场地
比赛对其他很多方面都有要求,更详细的规定请自行查看比赛通知。

二、解决方案

根据比赛要求,要能灵活移动,以穿越障碍;要能循迹,以按照最优轨迹移动;要能抓取,以搬放物品;要能识别物品摆放方式,以稳定抓取物品,那么最容易想到的自然是麦克纳姆轮无人小车,而且某宝上由很多开源成品,只需再设计抓取机构和物品识别就可以了。

我们将任务大致分为四部分:整体机构设计循迹与路径规划模块起升抓取模块取物识别模块,我本人则是负责循迹与路径规划这一部分,其他部分由别的小伙伴完成。控制芯片部分则是用了一块Arduino Mega 2560,搭配一块编码电机驱动板,负责循迹与路径规划控制;一块树莓派,控制起升抓取模块,取物识别模块。
下面一一说明具体的安排。

1、整体机构设计

整体机构设计是框架,是我们完成比赛的基础和承载体,它的好坏直接影响其他模块的设计、调试,同时决定了比赛结果的上限和下限。

整体机构设计的是否优秀,个人觉得可以从整体重量、控制难度、结构稳定性几个方面判别。

整体重量应越轻越好,一方面因为它直接与成绩中的重量分挂钩,另一方面越轻就意味着惯性越小,越容易提高控制精度;控制难度自然越低,对实现控制的同学水平要求越低,稳定性就越好,也更容易在不改变机械结构的前提下达到该结构的成绩上限;结构稳定性自然是越稳定越好,这里指的刚度要足够好,并考虑结构重心足够靠下,刚度不够,机构加减速过程中摇摇晃晃,控制难度自然直线攀升,而重心是比较容易忽视的点,比赛搬运的物品是重为1kg的牛奶箱,我们组作品的平均重量在10kg左右,搬运时重心会有改变,严重时会导致倾覆。

结合以上分析,我们采用了和上届参赛的师兄们大致相同的设计框架。以欧标铝型材为骨架,搭建一个长方体作为车体;在长方体上方架两根导轨,搭配同步带传动,实现抓取装置的水平移动;抓取装置则是步进电机驱动类似辘轳的机构,使得机械爪可以上下移动,再配和舵机完成机械爪的抓取动作;车体的移动则是四个减速编码电机驱动麦克纳姆轮实现。

基于这个整体结构,我们就可以在不改变车方向的前提下,实现从车的一头取物,另一头放物的方案,解决去了因为折返跑而导致的调头问题,控制起来也更容易。最终我们的成品如下图。

整体

当然,引用我导的一句话,任何设计都是平衡的艺术。设计一个机构,必然有很多的因素相互制约,一项指标非常好,一定有另一项或者另外几项项指标非常差,区别就在于各个指标对设计目标影响的程度。想要骨架材料强度又好重量又轻,必然只能多花钱买碳纤维材料,因此,在设计时应多方面,多层次的综合考虑,按照对比赛成绩影响的大小,决定每个参数的选取。

2、循迹与路径规划

该模块由我完成,我将其分为了三个部分,电机控制、循迹和路径规划。

首先是电机控制,即控制四个电机按照给定的速度运行,结合麦克纳姆轮运动学,实现车前进、横移和自转三种基本移动。关于麦轮运动学的帖子有很多,就不再赘述推导过程,只说结论:当轮子呈O型安装时,如图,四个轮子同向前转或后转,车就可以向前或向后移动;左前轮和右后轮向前转,左后轮和右前轮向后转,则可向右平移,左移同理,左前轮和右后轮向后转,左后轮和右前轮向前转,即可。
麦轮O型安装

电机控制板是Arduino mega 2560加电机驱动扩展板,Arduino系列屏蔽了很多底层驱动层面的东西,编程环境简单,上手难度对于熟悉51的同学来说稍有难度,而熟悉STM32的话则难度为零。该扩展板支持12v电压输入,只需一块可以输出12v的电源即可驱动,引出了所有的IO引脚,并特别引出了四个减速电机接线接口,方便接线,唯一的遗憾点在于减速电机驱动芯片单电机的平均输出电流只有1.2A,加之我们车结构设计的缺陷(该缺陷在下一部分会详细说明),最终比赛中横移时的电机转速并没有达到最大转速146rpm/min,没能达到该车结构的理论最好成绩,比较可惜。

Ardino Mage 2560

电机则是选用了一款霍尔编码减速电机,这里只列出参数:额定电压12v,减速比1:60,额定转速146rpm/min,额定扭矩0.2Nm,堵转扭矩0.9Nm,而霍尔编码器精度对本次比赛而言足够了。对于减速电机而言,一般有六个引脚:12v电源正负引脚,5v编码器电源正负引脚,二相编码器数据输出引脚。将12v电源接电机驱动芯片12v引脚,5v电源接5v电压,数据输出引脚接Arduino 的IO引脚据,控制时向电机驱动芯片输入不同占空比的PWM波,即可实现减速电机的调速。

电机

当然,虽然改变输入给电机驱动芯片PWM波的占空比可以改变电机转速,但是还要解决以什么方式改变占空比,或者说根据什么来改变占空比的问题。这里采用了PID控制中的增量式PI控制,使得实际转速快速跟随目标转速变化,这样既能满足麦轮对四个轮子转速的精度要求,也保证了车位置的精确,减少了机械爪控制的难度。具体的PI控制原理也已经有很多的帖子展示出来了,也不多做赘述。

下图是加入了增量式PID后实际转速(蓝线)跟随目标转速(红线)变换情况的实测图。该图是刚开始调PID时的图,P参数和I参数还不够好,而写本文时我懒得动 不方便(滑稽脸)再测一次最终参数的图,就先贴旧图,当然最终参数比这个表现好很多。
PI调速

这里贴出部分代码,以供参考。需要全代码的请私聊。

控制麦轮运动的函数很简单,根据运动学和目标转速设置每个轮子的正反转和转速即可,具体代码如下:

/*
* 描述:根据需要计算每个轮子的转速
* 输入:float Line_vel:前进速度
* 		float Pan_vel:横移速度
* 		float Angle_vel:自转角速度
* 输出: 无
* 备注:无
*/
    void Move(float Line_vel, float Pan_vel, float Angle_vel)
    {
        LF_Wheel_Spd = constrain((Line_vel + Pan_vel + Angle_vel *(BASE_WIDTH + WHEEL_DIAMETER)*0.5) , -MAX_RPM, MAX_RPM);
        RF_Wheel_Spd = constrain((Line_vel - Pan_vel - Angle_vel *(BASE_WIDTH + WHEEL_DIAMETER)*0.5) , -MAX_RPM, MAX_RPM);
        LR_Wheel_Spd = constrain((Line_vel - Pan_vel + Angle_vel *(BASE_WIDTH + WHEEL_DIAMETER)*0.5) , -MAX_RPM, MAX_RPM);
        RR_Wheel_Spd = constrain((Line_vel + Pan_vel - Angle_vel *(BASE_WIDTH + WHEEL_DIAMETER)*0.5) , -MAX_RPM, MAX_RPM);
    }

下面一部分是通过增量式PID调速算法核心。其计算公式为:
*△pwm(k)=P * e(k-1)+I*e(k) +D (e(k)-2e(k-1)+e(k-2))
其中P、I、D分别为比例、积分、微分参数,e(x)为第x次调节PWM占空比时实际转速与目标转速的误差,△pwm(k)为第k次调节占空比时与当前PWM占空比之差。
算法实际使用中省略了微分部分,具体原理我也没有搞清楚,但从实测的转速图来看,有没有这一部分转速控制的区别不大(不排除我实验做的有问题这种情况),欢迎有大佬指点,在此先感谢大佬了
具体代码如下。

/*描述:该函数为PI调速函数,将它作为10ms定时器的中断服务函数,就可以近似的求解微分,函数最后会求得新的pwm占空比值,并调用SetSpd()函数,改变对映轮子的pwm值。
*输入: int target:目标转速值。
*       int o :轮子编号,用于输出指定轮子的转速。
* 输出:无
* 备注:无
*/
 void Incremental_PID(int target,int o)
    { 
      updateSpd(o);   //通过编码器获取当前实际转速 ,并将值赋值给全局变量 int rpm。
      Error = rpm-target ;
      P_Error = Error-L_Error;
      I_Error = Error;     
      add = KP * P_Error + KI * I_Error ;
      Out_Pwm = Out_Pwm+add;
      L_Error = Error;

      if(Out_Pwm > MOTOR_MAX_PWM)  Out_Pwm = MOTOR_MAX_PWM;   
      if(Out_Pwm < -MOTOR_MAX_PWM) Out_Pwm = -MOTOR_MAX_PWM;
        
      if(target==0)
      {
       Out_Pwm = 0;
      }
      SetSpd(Out_Pwm);//更新当前输出的PWM占空比
    }

这一部分则是读取编码器值,并计算转速的函数。通过Ardino标准库中计量从系统开机到当前时刻所用时间的函数 millis(),再读取电机编码器的计数值,从而得到一段时间内车轮走过的距离,就得到了车轮一段时间内的平均线速度,当这一段时间足够短时,就可以视为车当前的瞬时速度。
具体代码如下。

/* 描述: 该函数可以通过编码器值计算出当前实际速度,并将值赋值给变量rpm。
*  输入:int o : 轮子编号,用与输出指定轮子的转速
*  输出:无 
*  备注:无
*/
void updateSpd(int o)
    {
      unsigned long current_time = millis();
      unsigned long dt = current_time - prev_update_time_;

      double delta_ticks = getEncoderPosition() - prev_encoder_ticks_;
      rpm = ((delta_ticks / COUNTS_PER_REV)*4*2*PI) / (0.001*dt); //计算车轮线速度
      
      prev_update_time_ = current_time;
      prev_encoder_ticks_ = getEncoderPosition();
    }

然后是循迹。我们采用七路光传感器加15mm白色反光带实现。其核心部分是led灯和光敏传感器,一个LED灯和一个光敏传感器为一组,共七组横向排列,长约10cm左右(见下图循迹模块反面)。当LED灯发出的光线由反射,到达光敏传感器的光强超过一定数值,该传感器输出的电平会由高变低,Ardino只要检测引脚的电平高低,就可以检测到反光带与传感器的相对位置。由于PVC塑胶地与黑白反光带对光线的反射率不同,经过测试,PVC塑胶地板的反光率介于白色反光带和黑色反光带之间,因此只要传感器到地面的距离合适,就可以识别出黑色反光带与地面,或白色反光带与地面,从而实现循迹。至于具体选择哪种颜色的反光带,要根据具体的路径规划而定。

下图为循迹模块正面。
循迹模块正面
下图为循迹模块反面。
循迹模块反面

代码部分的核心算法就很简单了。我们想要车中心沿着反光带前进,只要将传感器安装在车底面矩形四边的中点就可以了,反光带则是只用白色反光带。传感器在没检测到时返回给Arduino的值为1111111,当反光带处于在传感器正中间时,返回的数值为1110111,即不用调整;当反光带处于在传感器最左边时,返回的数值为0111111,即车需要向左自转;当反光带处于在传感器最右边时,返回的数值为1111110,即车需要向右自转。以此类推,只需调整传感器每个状态下车需要自转的速度快慢,就可以实现循迹了。
具体的代码贴出来。

/*
 * 描述:用前方的传感器循迹,使车向前移动。
 * 输入:无
 * 输出:无
 * 备注:无
 */
void sensor_front(void) //前传感器,白0
{
    s2_1 = digitalRead(senor2_1);
    s2_2 = digitalRead(senor2_2);
    s2_3 = digitalRead(senor2_3);
    s2_4 = digitalRead(senor2_4);
    s2_5 = digitalRead(senor2_5);
    s2_6 = digitalRead(senor2_6);
    s2_7 = digitalRead(senor2_7);
    motor_run2|=s2_7<<6;
    motor_run2|=s2_6<<5;    
    motor_run2|=s2_5<<4;
    motor_run2|=s2_4<<3;
    motor_run2|=s2_3<<2;
    motor_run2|=s2_2<<1;
    motor_run2|=s2_1<<0;
    switch(motor_run2)
     {
      case 0x7e:for(j=0;j<500;j++)Robot.ROS_Move(0, 0, -15);break;//1111110
      case 0x7c:Robot.ROS_Move(130, 0, -13);break;//1111100
      case 0x7d:Robot.ROS_Move(130, 0, -10);break;//1111101
      case 0x79:Robot.ROS_Move(130, 0, -7);break;//1111001
      case 0x7b:Robot.ROS_Move(130, 0, -5);break;//1111011    
      case 0x73:Robot.ROS_Move(130, 0,-3);break;//1110011
      case 0x77:Robot.ROS_Move(130, 0, 0);break;//1110111
      case 0x67:Robot.ROS_Move(130, 0, 3);break;//1100111
      case 0x6f:Robot.ROS_Move(130, 0, 5);break;//1101111
      case 0x4f:Robot.ROS_Move(130, 0, 7);break;//1001111
      case 0x5f:Robot.ROS_Move(130, 0, 10);break;//1011111
      case 0x1f:Robot.ROS_Move(130, 0, 13);break;//0011111
      case 0x3f:for(j=0;j<500;j++)Robot.ROS_Move(0, 0, 15);break;//0111111 
      default:{
      			for(i=0;i<100;i++)
            		{
              			for(j=0;j<10;j++) Robot.ROS_Move(130, 0, 0);
           			}; 
            break;
            }
}
#if 0
    Serial.println(motor_run2,BIN);
    Serial.println("\n");
#endif
    motor_run2=0;
} 

这里每个状态的自转速度也是我们花最多时间调整的地方,因为前进或横移的速度越快,自转速度不变的情况下车往传感器中点调整的速度越快,也越容易调整过头;同理,前进或横移的速度不变,自转速度不合理会导致车沿着循迹线左右摆动或需要很长时间才能稳定在中点位置,想要使车达到快速稳定的循迹需要细致的调整这一部分。

最后,路径规划则是我们重点攻克的部分,因为前面两部分都能找到相识的甚至现成的代码,稍作修改就可以用,但路径规划必须针对本次大赛规则设计,只能从零开始。

比赛的要求是从取物区分三次搬运牛奶箱到堆码区,且搬运过程中牛奶箱必须穿过两个障碍物。一方面,牛奶箱的初始位置则是抽签决定,按照三个牛奶箱必须有两个摞起来的规则,在不考虑横放竖放的前提下,共有六种情况。至于为什么不考虑横放竖放,是因为识物模块可以分辨出牛奶箱的摆放方式,并做出相应的抓取动作,因此就没必要了,而实际结果也确实如此。
另一方面,一共就三个牛奶箱和三个取物点,因此只要知道了一、二、三号取物点中任意两个位置的牛奶箱个数,就可以确定另一个位置的牛奶箱个数了,所以,车的起始点在二号取物点,在启动后检测一次二号点的牛奶箱个数,再横移至一号或三号取物点检测一次,就可以确定牛奶箱摆放的初始位置,这对于车规划最优路径有很大帮助。
至于堆码区放物则相对简单些,四号点要横放,五号点要竖放,六号点也是横放。
在这里插入图片描述

我们选则了“日”字型轨迹完成比赛,如图。
刚开始我的思维受限,认为必须先放堆码区四,再放堆码区五,最后放堆码区六,经过另一组的大佬英子提醒:可以按照四、六、五的顺序放物,这样就少走一段长度为从四堆码点横着移动到六堆码点的距离,这里也是特别感谢下英大佬的指点(大佬NB!!!)。

最终,只要把牛奶箱初始的六种情况对应的最佳路径都规划出来,再配合取物识别模块判断出牛奶箱的初始位置分布就可以实现智能识别和最优路径选择了。

而六种情况的最优路径规划的思路相似,就以一号取物区一个,二号取物区两个为例展示,其他情况类推即可。从B点出发,先检测二号取物区有几个箱子,并抓起一箱,然后横移至C点,并检测牛奶箱数量,这样就可以推断出牛奶箱分布情况;然后从C到E,从E到D再到F,完成第一箱搬运;从F点直接到A点,抓取一箱,再从A到D,D到E再到H,完成第二箱搬运;从H直接到C点,再从C到B,抓取第三箱,从B到C,C到E再到D,再到F,最后到G,放第三箱,完成比赛。
在这里插入图片描述

代码部分必须要结合全部的代码才能理解,就不贴出来了。
附一张比赛时贴线尺寸图留念。
在这里插入图片描述
至此,这一部分全部结束。

3、起升模块

剩的两个模块代码不是我写的,不过我也趁着调试空挡摸索了一下,就只大致说说感受和我摸索时写的代码。代码调试平台为Ardnino Mega 2560。

按照我们车的构想,起升抓取模块应该实现机械爪的沿车身长方向的水平移动和沿车高方向的竖直移动,以及机械爪捏合动作和牛奶箱横竖摆放所需的爪的旋转运动。抓牛奶箱,最简单也容易实现的办法时直接抓提手带,这也是官方默认的抓取方式,但提手带本身不大,所以车停止的位置精度和机械爪的移动精度一定要高,这也是我循迹模块调了很久的原因之一,而机械爪就选用了两个步进电机实现移动;爪子捏合和旋转要求力矩足够,且工作角度范围都不大,故选用两个舵机实现。
在这里插入图片描述

步进电机, 故名思意,按步前进的电机,一般通过驱动器控制,因为配合驱动器可细分步长和调节电流,改变电机的输出功率,提高控制精度。其控制策略为,给予电机一个pwm脉冲,则旋转一步,我们选择的是42步电机,即旋转42步,电机转一圈。因此,给的pwm脉冲越多,电机可以转越多的步,可以转越大的角度,即pwm脉冲数可以控制电机旋转的角度;而pwm波的频率,即一个完整pwm脉冲所用的时间,对应转速,频率越快,一个完整pwm脉冲所用的时间越短,电机转速越快。我是通过软件模拟输出pwm脉冲,这样脉冲周期和脉冲数都可以控制,对应的电机转速和移动的步长也可以控制了。具体的代码如下。

/*
 * 描述:控制步进电机。与电机连接的轮毂直径34mm。
 * 输入:num:要旋转的圈数;
 *      dir:旋转方向。1为顺时针(前进),0为逆时针(后退)。
 *      rpm:转速。应在0到180内。对应延时时间为100us到18000us。
 * 输出:无
 */
void MOTOR_steps(int l,int dir,int rpm)
{
   int  i,j;
   int  t=0;
   float steps_num ;
   int steps_num_p1 ,steps_num_p2;
   long timeold=0,timenew=0,dt,rpm_num;

   steps_num = 1600*(l/(3.1415926*DIAMETER_MOTOR));
   steps_num = steps_num+3*l/10;//修正误差,由实验所得。每移动1cm加3步,可能根据轮毂的直径改变而改变
   
   steps_num_p1=steps_num/1600;
   steps_num_p2=steps_num-steps_num_p1*1600;
  
   if(rpm == 0)
   {
      steps_num  = 0;
   }
   else
   {
    t=18000/rpm;
   }
 
  if(dir == 1)
  {
   digitalWrite(DIR, HIGH);
  }
  else if(dir == 0)
  {
    digitalWrite(DIR, LOW);
  }

  
  for( i=0;i < steps_num_p1 ;i++) //实测三组数据:延时t=100us,dt=330 ,rpm=180r/min 延时200us,dt=660 ,rpm=90r/min,延时500us,dt=1624 ,rpm=36r/min
  {     
      timeold= millis();  
      for( j=0;j<STEPS_MOTOR;j++)
      {
        digitalWrite(PUL, HIGH);
        delayMicroseconds(t);
        digitalWrite(PUL, LOW);
        delayMicroseconds(t);    
      }
    
  for( j=0;j<steps_num_p2;j++)
  {
    digitalWrite(PUL, HIGH);
    delayMicroseconds(t);
    digitalWrite(PUL, LOW);
    delayMicroseconds(t);    
  }
}

当然,我并没有研究的很深入,也有很多问题有待解决。
比如:1、转速是由延时的时间t决定的,而t可以突变,但实际中电机转速不可能突变,那就意味着电机变速时力会突然增大,导致电流剧增。工程中并不希望看到。应该实现平滑调速。
2、应该如何用步进电机控制麦轮呢?
3、程序中电机转速180可能并不是最大转速,但时间原因并灭有深入。

舵机的控制与步进电机不同,无论舵机处于哪个角度,给一个pwm,它就会旋转到pwm的占空比所对应一个角度,我们使用的舵机是180度行程,且速度不可调。附上代码:

#define servopin 10 

void setup() {
  // put your setup code here, to run once:
}

void loop() {
 delay(2000);
 analogWrite(servopin, 100);
 delay(2000);
 analogWrite(servopin, 180);
 delay(2000);

}

程序是硬件生成pwm波,当然软件模拟也可以。

4、取物识别

这几个字看起来高大上,其实就是两个激光测距模块,通过比较传感器到牛奶箱的距离,判断有没有牛奶箱以及其摆放方式,从而选择最佳路径。这个模块我没有调试,没动手实践也就不多废话了。

PS:看到这里我知道肯定有大佬想吐槽明明只控制两个步进电机和两个舵机却要用树莓派来高射炮打蚊子,简直是奢侈至极!这里容我狡辩一下,其实树莓派是上届师兄留下的,秉持有旧板子能用就不买新板子的原则,我们才做出这样的配置,虽然看起来确实是浪费了树莓派的性能,但是却节省了经费呐!所以,大佬们不要纠结这一点了。

三、反思与不足

1、结构

(1)、车轮“外八”问题。
从整体来看,我们的车体可以满足比赛要求,而且也不是很重,但在调试过程中,我们发现,车轮的结构不够合理,四个车轮因重力而受额外的扭矩,导致车轮“外八”,增大了麦轮横移时的阻力。经实际测量,在电机满速的百分之七十转速下,车横移四个电机所需的电流比前进大了近三倍,出于对驱动芯片的保护,比赛横移的速度收到了很大限制。我们也考虑国过改进,将车轮放在车框架底面四角的正下方或尽量靠近车体,以减少或是消除重力带来的力臂,但还是没有找到合适的实现方案,加之备赛时间紧迫,就没有改进了。
(2)、麦轮悬空问题。
大家都知道麦克纳姆轮对四个轮子的转速精度要求较高,所以如果有轮子悬空,那么地面就不能提供足够的摩擦力来保证车子以理想状况受力,从而不能正确的移动。实际比赛场地并不能保证一定平整,所以如何在场地不平整的情况下保证麦轮还可以与地面充分接触就是个大问题。我们想到的方案是加独立悬挂,然而价格、可靠性、安装复杂度都不太令人满意,还好比赛时线上赛,我们有时间选一块基本平整的地方做比赛场地,这个问题也就搁置了。

2、电池

我们原本的方案是一块大电池提供所有的能源,而正好有一款有5V usb、12V /5A、24V/10A三种输出接口,以为足够了,结果因为前面所说的“外八”问题,导致12V /5A输出功率不够,甚至出现横移动不了的情况,要不是又另一组大佬鹏哥的提醒,我很难发现这个问题。最后加装一块12V/9A的电池后,虽然没有从根本解决问题吧,好歹能完成比赛了。

3、思路

这是我第一次作为主要技术参加国赛,没有一点经验,刚拿到题目时脑袋乱轰轰,不知道从哪里着手开始,只能先抓住一个个点,弄清楚每个点后再串联起来。

现在回头看看备赛全程,觉得做比赛应该是个先从上到下再从下到上的过程。先从上到下,就是把目标逐一分解成模块,从电控可实现的角度提出多种解决方案,以此敲定一个可实现的整体方案,程序层面就需要写出对应的底层驱动并加以测试和调整;再从下到上,就是将一个个模块组装,通信,最终成为完全体,程序层面就是理清模块与模块间的逻辑调用关系,组成完整的可以完成比赛目标的代码。
这期间,避免不了来来回回的推翻方案重演,代码不断的迭代,这都意味着之前努力的白费;更避免不了队员之间意见不统一,激烈的争辩,甚至团队凝聚力下降,但只要大家都是为了更好的成绩,都无可厚非。反过来想想,每次所想与实际不符时,每次调代码发出“这居然能动???”或者“这居然动不了???”的置疑时,每次发现自己选择参数或材料或结构考虑不全面给自己挖的坑时,那种抑制住内心想煽自己巴掌的狂躁与无奈,拼命让自己冷静下来,好集中注意力到解决问题上来的状态不正是工科的魅力之所在吗?
我很享受这段做比赛的时光,感谢时运给我这么一次经历。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐