Quantcast
Channel: 英特尔开发人员专区文章
Viewing all articles
Browse latest Browse all 583

月亮船之旅

$
0
0

有一个团队,来自三个国家的五位落单的参赛者凑成一桌,各自互补,没有一个多余的,也少不了任何一个,我们叫"有一个团队"。

Note:这张照片使用全景自拍机拍摄。全景自拍机本是用于这个比赛的备用项目。
 

概念阶段

项目开始,我们并没有分工,而是筛选大家带来的概念,概念不同,各自的分工也不同。我们拥有大概5个概念,为每个项目从创新性和容易程度进行筛选,并且讨论每个项目的可能的形态和需要花费的时间。最终决定制作开源婴儿摇篮床,主演用于简单监控婴儿的状况以及摇晃。参考评分标准,我们对项目的选择标准是创新性、能解决问题、尽可能有效用到英特尔Edison芯片的优势和传感器,最后一个就是一定要有趣。
该创意的提供者Rahul提供了一些数据支撑整个项目的进展,主要包括全球新生儿死亡率的数据,原因等等。从这一次的时间来看,项目周期实际可用时间只有20个小时,我们必须在有限的时间内制作一个能够解决实际问题的完整方案。

Note:Edison的包装还是很有仪式感的,Geek风浓厚。
 
现场的水果明显供不应求

团队分工阶段

由于项目概念来自医学背景的Rahul,这次产品经理他是最合适角色。Chris熟悉Arduino和Grove传感器,Edison提供了兼容Arduino的开发环境,现场也提供了大量代码范例,可以几乎无成本从Arduino切换到Edison平台上面来。Neil熟悉应用开发,Yong则是结构设计工程师,另外Luke是工业设计师,可以给我们的项目锦上添花。
我们的团队分工如下:
•    Chris,队长,负责Edison的程序和协调
•    Rahul,产品经理,负责产品的功能需求
•    Neil,程序员,负责云端服务的开发
•    Yong,结构工程师,负责结构和传动的设计
•    Luke,工业设计师,负责产品造型设计
•    Sandy,志愿者,负责其他
因为luke临时有事,我们最终没有用上那个更帅的造型。巧手的志愿者Sandy则帮了节约了许多时间制作材料。Rahul除了把提供项目方向上的支持,另外一方面也准备了用于介绍我们项目的介绍,对于短项目来说,七分靠实力,三分靠演示,如何不能在8分钟内让评委明白我们做了什么,那一切都没有意义。

需求分析阶段

整个项目的需求在两天的过程中多次调整,一方面是产品经理Rahul在调研过程中不断有新的发展,另一方面我们原有的一些想法没有在现场找到相应的元件。这也是我们平时工作的日常。最终需求如下
•    作为母亲,希望知道婴儿的睡姿是否正确,以防止窒息。
•    作为母亲,希望婴儿床能自动补偿被褥的温度
•    作为不在婴儿身边的母亲,希望知道婴儿是否在哭,是否活动状态
•    作为母亲,希望能够自动摇晃婴儿床。
•    作为母亲,希望第一时间知道婴儿不易察觉的情况,如大小便,发烧
•    作为母亲移动用户,希望知道远程查看婴儿床的基本状况
•    作为母亲移动用户,希望能够收到紧急状况的报警。
•    作为该产品的制造者希望极大程度降低制造成本,
•    作为该产品的发明者希望发展中国家的制造者也可以使用常见的材料进行自行制造
我们还考虑过增加蓝光来治疗部分新生婴儿黄恒的功能,但是由于没有Led灯条放弃了这个功能。由于是创客马拉松,迎合主办方的主旋律和评分规则也是理所当然,但是这和实际项目的研发是产生冲突的。实际项目绝对不会为了使用技术方案去增加需求。这也使得我们的部分传感器使用的需求有些画蛇添足。而产品形态方面,我们也纠结了很久,到底是一个穿戴设备,还是一个摇篮床,还是两个一起。可穿戴的数据更准确,而作为床则更安全,最终我们两个都用了,现场时间有限,并未能开发出可穿戴传感器部分,则直接从Edison连接了一个有线传感器进行demo。

技术方案确定阶段

准确的说,确定方案和需求是同步进行的。我们把技术方案分成了几大块,这也方便我们进行分工和准备材料。

 
传感器输入部分:用于读取环境和婴儿自身的一些数据,我们选择了套件内的Sound,Water,Ambient,Vibration,3-Axis,Temperature。
输出部分:用于显示传感器的状态,选择了套件自带的三颗Led和I2C LCD,然后申请了Ledbar和Relay,用于机械传动和电辅加热电路部分。
云平台:用于远程查看传感器的数据,方案确定时并未决定用哪家的云服务,最简单的方案是使用Edison作为AP自启一个Service,这样能够保证整个系统的独立性和稳定运行。我们现场有两块Edison板,所以是Neil可以独立开发云平台的代码,Chris则把所有需要输出的变量放在一块,方便第二天进行整合。
传动部分:对于结构工程师Yong来说制作一个摇晃的机械结构并不是一个难事,但是现场要找到扭力能够驱动摇篮床的电机就有点难度了,于是乎我们申请了直流电机和一个水泵。因为水泵的马达看起来好像很大只,留着备用
结构设计:主要是摇篮床的材料和结构。既需要轻,又需要能够结实到可以承载一个婴儿。这也是最有趣的一个部分。

Let's Rock

在安装完环境之后,大家就开始了各自的工作。
传感器读取部分进展的非常顺利,得力于Grove套件的即插即用和完善的Wiki。由于有些故事实在太牵强,则去掉了Ambient传感器,另一方面,Vibration传感器并不好用,换做了3-Axis传感器来判断婴儿的睡姿和身体活动。传感器的输出方面,使用了几颗不同颜色的LED灯来显示不同的状态,其实也是方便调试代码。我们有三个状态,红色显示婴儿头朝下睡了,蓝色显示尿不湿需要更换了,绿色则显示室温太低,辅热正在工作。官方套件的LCD并未调试成功,所以申请了一个LEDbar用于显示环境温度,反而效果更好,现在看来太多给传统XX产品加个屏幕就自称智能XX产品,所以去掉屏幕反而略显专注。
 
在云服务方面,我们尝试了多种方案,我们第一天并不是很顺利。

在结构方面,设计好相应的图纸之后,在现场并未找到材料,意启有很多很漂亮的纸板可惜不给我们。这次参赛的很多作品都是提前做好的,所以除了3D打印机现场并未提供结构方面的材料。无赖我们只能回家去拿来了纸板,顺带拿了保鲜膜,工具,加热器,胶带等工具。

第一天大概做到10点,就各自回家了,我们电路部分基本完成,结构和传动也有了方向,我们离开的时候,现场还有不少团队仍然奋斗在一线。

Note:拿材料三公里路程,来回堵了1个多小时,南山的繁华实在是让人陶醉。另外我们还拿回来一个。。。蒙奇奇,为第二天的卖萌做准备。

晚上我们就近在意启咖啡点了三明治和华夫饼,分量实在太小,又点了三个水煮蛋,画风不忍直视,瞬间饱了。

Continue Rock

一大早,我们就赶过来了。Neil昨晚准备了web的一些代码,继续调试中。

Rahul 还在倒腾PPT,我们只准备了五页的PPT。

•    我们要解决的问题和一些数据
•    现在的方案
•    现在方案的不足
•    我们方案的优势
•    团队介绍
 
 
Yong的床主体架构有了新的进展。


 
 床的主体完成对于我们来说是一个非常重要的里程碑。但是在这个时候,我还是不知道传动系统到底是如何设计的,Yong脑子里肯定已经有一个完善的计划。
 
配合一个婴儿哭的背景音乐,很是吸睛。
 
接下来是结构和电子的部分的整合。最下方的是5V辅热器,虽然是一个争议的方案,但是作为demo我们还是把他保留了下来。配合温度传感器保证环境温度适合。

然后上面做防水,我们用了是最常见的保鲜膜,保鲜膜上面才是Water传感器,然后上面是被褥。由于仅仅只是演示,如果是真实产品那估计要很大排量才能检测得到。

帽子下面的3-Axis传感器,理想的情况应该是制作成一个无线的帽子。

灯放在侧面,依次是温度传感器,睡姿报警灯,辅热灯和尿不湿灯,最下方的是马达驱动板。

接下来就是传动部分了。Yong在电机上面安装了一个曲轴,然后曲轴拉扯摇篮进行摇摆。

最开始我们把电机安装在上方,但是扭力不够,又改成正转再反转,但是节奏很难把握。后来把马达改到下方,整个摇篮摇动起来才非常顺畅了。

 

现场环境比较吵,我们反复调整Sound传感器的阈值,使得手机播放婴儿哭声的时候,摇篮床能自己动起来,而平时不会受环境影响。当摇篮床听到哭声就自己摇的时候,我们都非常激动。

另外我们设定的逻辑是,如果婴儿停止哭了,那么摇篮会在几秒后停止摇动。

另一头,在云服务我们还是有些瓶颈没有解决。我们临时决定换做使用串口先进行演示传感器数据的输出。保证整个功能的完整性。

我们对整个系统进行了多次测试,包括更换婴儿姿势,测试water传感器,多次播放婴儿哭声来保证马上到来的演示不出差错。

同时优化了部分接线,Grove自身的线太短,我们自己制作了长的Grove接线用于部分传感器。

至此,项目基本完成。等待评委们的审阅。

Note:过程中南山电视台过来采访,Rahul彪中文。

项目演示

整个项目演示还算比较顺利,我们快速介绍了PPT中的内容和团队,然后演示了项目的基本功能,如婴儿睡姿报警,尿不湿报警,婴儿哭的时候摇动摇篮床等等。蒙奇奇配合婴儿的哭声和摇篮床的摇动确实很萌化。而云服务我们并没有在现场成功演示,这是最大的一个遗憾。

这个需要赞一下评委,评委测试了我们的LEDbar是不是真的会随着温度变化,我们也进行了演示,确实随着温度会变化。这种马拉松,使用hard code作弊是可能的,检测每个传感器是否真的工作能够保证评分的公平性。
 
最后我们的项目获得了三等奖,我们非常的开心。

总结

评委问了我们未来的商业计划,我们的回答是,我们项目本身就是基于发展中国家的一个低成本摇篮床,我们并不希望其商业化,这两天的过程就是我们的收获,认识了很多有意思的人,看到了很多有意思的项目。不是每个项目都需要商业化,像这种2天时间拍脑袋想出来的短期项目,缺少沉淀,也不可能商业化,分享整个过程和开源才能最大化其我们这两天付出的价值,这也是为何你能看到这篇文章。项目仓促,诸多硬伤请多包涵。

谢谢Intel,SeeedStudio,还有导师们给我们的许多帮助。两天愉快的月亮船之旅。

:项目用到的电子原件
所有原件可以在Seeedstudio 官方网站购买。
•    Edison + Grove Expansion 
•    Water传感器
•    3-Axis传感器
•    Temperature传感器
•    LEDbar
•    LED x3
•    Relayx1
•    直流电机x1
•    电机驱动板x1
    
:项目用到的其他材料
•    硬纸板
•    胶带
•    订书机
•    塑胶杆
•    蒙奇奇
•    被褥
•    保鲜膜
•    加热器

:项目的代码
#include <Grove_LED_Bar.h>
#include <Wire.h>
#include "MMA7660.h"
MMA7660 accelemeter;
Grove_LED_Bar bar(9, 8, 1);

int relayPin = 6;
int relayLed = 7;
int soundLed = 3;
int waterLed = 3;

int zLed = 4;
int zState = 0;
int8_t x;
int8_t y;
int8_t z;
float ax, ay, az;
int shakeCounter = 0;
long lasttime = 0;

#define MotorSpeedSet             0x82
#define PWMFrequenceSet           0x84
#define DirectionSet              0xaa
#define MotorSetA                 0xa1
#define MotorSetB                 0xa5
#define Nothing                   0x01
#define EnableStepper             0x1a
#define UnenableStepper           0x1b
#define Stepernu                  0x1c
#define I2CMotorDriverAdd         0x0f   // Set the address of the I2CMotorDriver
// set the steps you want, if 255, the stepper will rotate continuely;
void SteperStepset(unsigned char stepnu)
{
  Wire.beginTransmission(I2CMotorDriverAdd); // transmit to device I2CMotorDriverAdd
  Wire.write(Stepernu);          // Send the stepernu command
  Wire.write(stepnu);            // send the steps
  Wire.write(Nothing);           // send nothing
  Wire.endTransmission();        // stop transmitting
}
///////////////////////////////////////////////////////////////////////////////
// Enanble the i2c motor driver to drive a 4-wire stepper. the i2c motor driver will
//driver a 4-wire with 8 polarity  .
//Direction: stepper direction ; 1/0
//motor speed: defines the time interval the i2C motor driver change it output to drive the stepper
//the actul interval time is : motorspeed * 4ms. that is , when motor speed is 10, the interval time
//would be 40 ms
//////////////////////////////////////////////////////////////////////////////////
void StepperMotorEnable(unsigned char Direction, unsigned char motorspeed)
{
  Wire.beginTransmission(I2CMotorDriverAdd); // transmit to device I2CMotorDriverAdd
  Wire.write(EnableStepper);        // set pwm header
  Wire.write(Direction);              // send pwma
  Wire.write(motorspeed);              // send pwmb
  Wire.endTransmission();    // stop transmitting
}
//function to uneanble i2C motor drive to drive the stepper.
void StepperMotorUnenable()
{
  Wire.beginTransmission(I2CMotorDriverAdd); // transmit to device I2CMotorDriverAdd
  Wire.write(UnenableStepper);        // set unenable commmand
  Wire.write(Nothing);
  Wire.write(Nothing);
  Wire.endTransmission();    // stop transmitting
}
//////////////////////////////////////////////////////////////////////
//Function to set the 2 DC motor speed
//motorSpeedA : the DC motor A speed; should be 0~100;
//motorSpeedB: the DC motor B speed; should be 0~100;

void MotorSpeedSetAB(unsigned char MotorSpeedA , unsigned char MotorSpeedB)  {
  MotorSpeedA = map(MotorSpeedA, 0, 100, 0, 255);
  MotorSpeedB = map(MotorSpeedB, 0, 100, 0, 255);
  Wire.beginTransmission(I2CMotorDriverAdd); // transmit to device I2CMotorDriverAdd
  Wire.write(MotorSpeedSet);        // set pwm header
  Wire.write(MotorSpeedA);              // send pwma
  Wire.write(MotorSpeedB);              // send pwmb
  Wire.endTransmission();    // stop transmitting
}
//set the prescale frequency of PWM, 0x03 default;
void MotorPWMFrequenceSet(unsigned char Frequence)  {
  Wire.beginTransmission(I2CMotorDriverAdd); // transmit to device I2CMotorDriverAdd
  Wire.write(PWMFrequenceSet);        // set frequence header
  Wire.write(Frequence);              //  send frequence
  Wire.write(Nothing);              //  need to send this byte as the third byte(no meaning)
  Wire.endTransmission();    // stop transmitting
}
//set the direction of DC motor.
void MotorDirectionSet(unsigned char Direction)  {     //  Adjust the direction of the motors 0b0000 I4 I3 I2 I1
  Wire.beginTransmission(I2CMotorDriverAdd); // transmit to device I2CMotorDriverAdd
  Wire.write(DirectionSet);        // Direction control header
  Wire.write(Direction);              // send direction control information
  Wire.write(Nothing);              // need to send this byte as the third byte(no meaning)
  Wire.endTransmission();    // stop transmitting
}

void MotorDriectionAndSpeedSet(unsigned char Direction, unsigned char MotorSpeedA, unsigned char MotorSpeedB)  { //you can adjust the driection and speed together
  MotorDirectionSet(Direction);
  MotorSpeedSetAB(MotorSpeedA, MotorSpeedB);
}


// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  bar.begin();
  accelemeter.init();

  pinMode(relayPin, OUTPUT);
  pinMode(zLed, OUTPUT);
  pinMode(soundLed, OUTPUT);
  pinMode(waterLed, OUTPUT);
  accelemeter.getXYZ(&x, &y, &z);

  zState = z;
  Wire.begin();
  MotorSpeedSetAB(0, 0); //defines the speed of motor 1 and motor 2;

}

// the loop routine runs over and over again forever:
void loop() {
  accelemeter.getXYZ(&x, &y, &z);
  // read the input on analog pin 0:
  int tempSensor = analogRead(A3);
  int soundSensor = analogRead(A2);
  int waterSensor = digitalRead(5);
  tempSensor = 1 / (log((float)(1023 - tempSensor) * 10000 / tempSensor / 10000) / 3975 + 1 / 298.15) - 273.15;
  int barLevel = int((tempSensor - 8) / 2);
  //Serial.println(tempSensor);
  //water alarm
  if (waterSensor) {
    digitalWrite(waterLed, LOW);
  } else {
    digitalWrite(waterLed, HIGH);
  }


  //temp alarm
  bar.setLevel(barLevel > 0 ? barLevel : 1);

  if (tempSensor < 20) {
    digitalWrite(relayPin, HIGH);
    digitalWrite(relayLed, HIGH);
    Serial.print("Heater:");
    Serial.println("On");
  } else {
    digitalWrite(relayPin, LOW);
    digitalWrite(relayLed, LOW);
  }


  //shake when detect sounds
  if (soundSensor > 350) {
    Serial.println("Crying");
    //Serial.println(z);
    MotorSpeedSetAB(100, 100); //defines the speed of motor 1 and motor 2;
    delay(10); //this delay needed
    MotorDirectionSet(0b1010);  //"0b1010" defines the output polarity, "10" means the M+ is "positive" while the M- is "negtive"
    shakeCounter = 0;
  } else {
    shakeCounter++;
    if (shakeCounter > 50) {
      shakeCounter = 0;
      MotorSpeedSetAB(0, 0);
    }
  }
  //direction alarm
  if (z < -12) {
    digitalWrite(zLed, HIGH);
  } else {
    digitalWrite(zLed, LOW);
  }
  //motion alarm
  if (abs(zState - z) > 10) {
    Serial.print("Motion:");
    Serial.println(zState - z);
    zState = z;
  } else {
    zState = z;
  }

 

  Serial.print("Temp:");
  Serial.println(tempSensor);

  Serial.print("Wed:");
  Serial.println(waterSensor);
  delay(100);        // delay in between reads for stability
}


Viewing all articles
Browse latest Browse all 583

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>