【教学】可变电阻控制几何体形态(GH+C#+Arduino)


#1

很多同学对硬件控制 GH 很感兴趣,正好今天有同学自己写程序连外设遇到点小问题,那就正好解答问题顺带做个教程了。这位 Zzzz 同学的帖子如下:

http://bbs.shaper3d.com/thread-24815-1-1.html

##Read me##
本教学中的例子,用 Grasshopper 的 Firefly 插件做是非常简单的,但是这篇教学不用Firefly,而是用C#写脚本来实现。

如果不是对C#程序感兴趣,只是想做这种控制效果的话,这个教学粗略浏览一下,当作了解制作过程就好,然后直接去下载 Firefly,Firefly 使用简单而且很强大很稳定。Firefly 的中文教程点这里

##教学##

我们要制作的就是通过旋转可变电阻的旋钮来改变Grasshopper在场景中生成物件的形态。效果如下:

一、连线和原理概述

我们要用到的材料是一块Arduino UNO控制板、一个可变电阻,一块面包板和若干根电线。

首先按照下图所示将材料全部连接起来。

上面这张图中连线的意思是,可变电阻最外面的两头,连接在电源上,即红色线连的是5V的电源输出,黄色线连接的是GND地线。中间的蓝色线是模拟信号,接到控制板的A0模拟信号输入接口。模拟信号在这里其实就是电信号,控制板会按照电流或电压的不同,将这里输入的电流和电压变化翻译成0到1024之间的数字。

那么这个例子的整体思路,就是把电阻输出的模拟信号,通过控制板翻译成数字以后发送给电脑,我们在电脑上用Grasshopper中的C#脚本读取这个数字,把这个数字作为影响形态的参数用在几何体上就可以实现用电阻控制形态了。

二、上传代码

将下面的代码上传到Arduino控制板中,如何给Arduino上传代码属于 Arduino 硬件使用的基础知识,互联网上有非常多的资料介绍,firefly的帮助文档中也有很详细的描述,这里就不赘述了。

大家可以到firefly的官方网站下载firefly和帮助文档。


int potpin = 0;
int val=0; 
void setup()
{
   Serial.begin(9600);
}
    void loop()
{
   val=analogRead(potpin);
   Serial.println(val);
   delay(200);
}

上面这段代码的意思是每隔200毫秒,读取电阻输入进来的信号转成数值,然后把数值送到串口中去。

那么接下来我们就要学习最重要的一步了,如何把这个数值读取到 Grasshopper 中。

三、通过C#脚本读取信号

在写代码之前我们先解答一下 Zzzz 同学的问题,这位同学的代码如下:

System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort();
sp.PortName = "COM3";
sp.BaudRate = 9600;
sp.Open();
int val = sp.ReadByte();
A = val;
sp.Close();

他遇到的问题是代码可以执行,但是只要接上外设,就会非常的卡,拔掉外设就好了,这是什么原因呢?

我们先逐行来分析一下这段代码:

System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort();
这句的意思是实例化了一个SerialPort(串口)对象,命名为sp,这个串口对象就可以看成是计算机和外设交互数据串口的代表。

sp.PortName = “COM3”;
这句把串口的名称设置COM3,也就是说要用sp这个串口对象代表哪个串口。
通过硬件管理器查看一下,正好arduino在我的计算机上也是COM3

sp.BaudRate = 9600;
设置波特率为9600,接入不同的设备,按设备波特率的不同设置为相应的值。

sp.Open();
这句是打开串口。

int val = sp.ReadByte();
A = val;
这两句是从串口读取字节,然后把读取到的值赋值给C#电池的输出变量A。

sp.Close();
这句是关闭串口。

了解了每个语句的用途,我们再回过头来看这个连线图

从图中可以看到,为了让代码能够不停的从COM3中读取数据,连接了一个Timer,每隔20毫秒就会执行一次脚本,这样做是没有错的,但是接了Timer 以后,代码中的sp.Open()和sp.Close()每隔20毫秒就会执行一次,也就是说每20毫秒就要把串口COM3打开并关闭一次,在如此段的间隔内打开关闭硬件接口是非常消耗资源的,这就是卡的原因所在了。

下面我们就动手来改代码

为了不用每执行一次就打开关闭一次串口,我在电池外面做了一个开关,需要的时候,从外面打开,代码里面主要负责读取数据,用完了,从外面关掉就好了,不用每执行一次就打开关闭一次。

这个电池里面的代码如下:

    System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort("COM3", 9600);
    if (open)
    {
      if (sp.IsOpen == false) sp.Open();
      if (sp.IsOpen) Msg = "端口已开启";
      int data = sp.ReadByte();
      A = data;
    }
    if (open == false)
    {
      if (sp.IsOpen) sp.Close();
      if (sp.IsOpen == false) Msg = "端口已关闭";
      sp.Dispose();
    }

虽然现在已经不卡了,但是用现在这个,有时还会出现错误,关闭端口以后马上打开,会发生打不开的情况,程序会告诉你端口被占用。这种情况打开关闭五次,就会出现至少两次打不开,就像这样:

这是因为每执行一次代码,就实例化一个串口对象,在打开串口的时候,之前一次的串口对象并没有完全被系统释放,依然占用着串口,再打就打不开了。

那我们就不要不停的去实例话串口对象,实例化一次就好了。要如何做呢?
大家看我这个图就明白了:

我们把实例话的过程放在左边的电池里,也就是说整个运行过程只需要在左边实例话了一个串口对象,传递到右边的电池中就好了,右边只是负责打开和关闭端口以及读取数据。这样打开关闭点的再快也不会出错了。

左边电池的代码:


System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort("COM3", 9600);
A = sp;

右边电池的代码:


   System.IO.Ports.SerialPort sp = ( System.IO.Ports.SerialPort) port;

   if (open)
   {
 
     if (sp.IsOpen == false) sp.Open();
     if (sp.IsOpen) Msg = "端口已开启";
 
     string data = sp.ReadExisting();
     if (data.Contains("\r"))
     {
       string[] sArray = data.Split('\r');
       A = sArray[0];
     }
     else
     {A = data;}
 
   }
   if (open == false)
   {
     if (sp.IsOpen) sp.Close();
     if (sp.IsOpen == false) Msg = "端口已关闭";
     sp.Dispose();
 
   }

注意,我为了读到电阻发来的信号,这里读取数据用的是sp.ReadExisting()

好了,现在打开串口以后,就可以正确读出数据了。

四、用串口数据控制形态

现在就可以把我们写好的脚本放到做好的sketch里面用电阻去控制形态了,这一步学过Grasshopper的同学都懂得。

例如最前面视频里演示的放到一个更具输入值不同变换高度的。

做好的在附件里面,Enjoy!
串口影响高度.zip (15.8 KB)