2010年02月 文档列表

Arduino开发板实验四:串口通讯,通过计算机控制舵机

前几天肩膀可能是拉伤了,休息了几天。先回答一个朋友们比较关心的问题,那一对直流电机的扭矩差不多是30kg*cm。前几天我已经接上电源,并且压了一些重物,看上去在室内平地可以轻松带动(爬坡没有测试)。因为还没有写电机的遥控程序,所以只是用原地打转做测试。按照我的计划,笔记本电脑将会作为一个配件,安装在小爱的胸前。所以今天做了一个串口通信的小实验,将来也要通过计算机这样控制小爱的各种运动。

实验目标是:做一个小程序,通过鼠标拖拽来控制舵机的角度,计算机和Arduino实验板之间使用USB线连接。

首先看一下接线图:

舵机接线图

舵机接线图

舵机控制的命令可以参考之前的小实验:Arduino开发板实验三(舵机控制)。先看看我们这个实验里需要用到的几个串口通信命令:

Serial.begin(9600);  //设置波特率
Serial.available();  //如果有数据传来,这个值大于0
int readValue = Serial.read();  //读取一个字节的数据

这个实验里只需要从计算机接受数据,如果需要发送数据的话,可以使用下面的命令:

Serial.print();
Serial.println();
Serial.write();
Serial.flush();

具体的用法可以去Arduino的官网上查看: http://arduino.cc/en/Reference/Serial

在Arduino端的程序如下:

void loop()
{
  while (Serial.available() > 0)
  {
    //读取计算机发送的角度值
    readValue = Serial.read();
  }
  //发送50个脉冲
  for(int i=0;i<50;i++)
  {
     //引用脉冲函数
     servopulse(readValue);
  }
}

计算机端的程序使用C#编写,Arduino使用USB连接计算机以后,其实也被映射到一个端口,所以连接的方法和蓝牙连接类似,可以参考萝卜头的蓝牙通讯部分。

下面是小程序的界面:

计算机上运行的小程序

计算机上运行的小程序

创建一个从0到180的轨道条(TrackBar,也有人叫它滑尺),当滑尺位置变换时,就把设置的角度发送到指定的端口上。代码如下:

        private void AngleTrack_Scroll(object sender, EventArgs e)
        {
            ReadValue.Text = AngleTrack.Value.ToString();
            if (serialConn != null && serialConn.IsOpen)
            {
                BlueToothDataSend(new byte[] { Convert.ToByte(ReadValue.Text) });
            }
        }

        private SerialPort serialConn;
        private void ConnectButton_Click(object sender, EventArgs e)
        {
            serialConn = new SerialPort();
            ConnectButton.Enabled = false;
            serialConn.PortName = PortNumber.Text;
            serialConn.Open();
            serialConn.ReadTimeout = 10000;
        }

        private void BlueToothDataSend(byte[] data)
        {
            int length = data.Length;
            byte[] readData = new byte[length + 2];
            readData[0] = (byte)(length % 255);
            readData[1] = (byte)(length / 255);
            for (int i = 0; i < length; i++)
            {
                readData[i + 2] = data[i];
            }
            serialConn.Write(readData, 0, length + 2);
        }

实验结果:拖动轨道条可以实现对舵机的控制,但是在0~30和150~180的区间,舵机是不动的;在30~150区间,舵机基本能按照鼠标拖动的角度旋转。结果表示串口通信成功了,但同时也说明这个舵机的实际转角只能达到120度左右,并且控制并不是特别精确。这意味着将来做机械臂的时候,可能会有很大的误差。头大中…..早知道就选步进电机了,不过一口吃不成胖子,慢慢来吧,硬件的误差可以通过软件来弥补。

工具啊工具

今天早上一直在露台上锯东西,下午右肩膀开始酸痛,晚上已经疼到举不动东西,估计接下来的几天要歇着了。据老丈人说,我那个锯弓不稳,锯条齿太细,所以锯起来太累。下回送我什锦锉的时候,顺便再送我几根粗牙锯条。

这几天北京大风降温,我在露台上叮叮咣咣的干了几天活,冻了个半死。便携式的电钻真是不够结实,打十几个孔以后钻头就开始打滑;砂轮也是,磨一根45#钢的轴,能把砂轮片打掉一半。这要是在工厂,连磨带钻也就是一两个小时的时间。

果然是“工欲善其事,必先利其器”。过段时间准备买一个小型台钻,这样打孔能方便点。如果有北京的朋友可以联系到加工厂或者车间,能够方便实惠的加工点东西,希望给我介绍一下。多谢了!

春节快过完了,休息几天就得收心回去上班了。发一张目前进度的照片吧,过几天再发详细点的攻略。

有点雏形的小车底盘

有点雏形的小车底盘

底盘小车的制作 – 轮子

春节期间老丈人和丈母娘来北京了。本来我还非常担心,如果老人们发现我每天捣腾这些东西,一方面可能觉得我不务正业,一方面也可能会觉得自己的闺女生活在水深火热之中。

老丈人曾经做过十多年的铣工,知道我这点小爱好以后,非常有兴趣,这几天一直跟我一起探讨和制作机械部分,还说回家以后要送我一套什锦锉。哈哈,也许男人就是天生的机器人爱好者 :D
废话不多说了,先看看最近几天的进展。

首先是订制的几根45#钢的小轴,春节前终于送到了。下面是我手绘的加工图和送到的小轴:

小车的轴

小车的轴

非常杯具的事情:我只标注了Φ8的直径,没有标注公差,所以寄来的小轴比轴承粗了0.2毫米。用磨刀石磨了半天,发现45#钢真是太硬了,这样下去胳膊磨细了轴也细不了。用砂轮打的话,又担心打不圆。经过和老丈人的讨论,决定制作一个小型“车床”,用来打磨这根轴。正好可以用小车的电机来驱动。这是从百宝箱里搜刮出来的各种杂物:

各种车床配件

各种车床配件

家里最可怜的菜板,一直被我用来做各种实验,这次它会变成一个车床。

百变菜板

百变菜板

直流电机需要一块角铁做支架,百宝箱里找不到合适的配件,我在家里寻摸了半天,终于发现电脑桌下面有几块合适的角铁,趁着老婆去午睡的机会把它拆了。作为补偿,让电脑桌也上个镜头:

电脑桌下面的角铁

电脑桌下面的角铁

看,和直流电机简直就是天生的一对

直流电机的支架

直流电机的支架

再用一块不知道从那里捡来的T型铁皮做成支架,一个简易的小车床就制作完成了:

小型车床

小型车床

接下来就是用砂轮打磨了,老丈人亲自操刀上阵,看看战斗现场:

打磨短轴

打磨短轴

经过打磨的短轴终于可以安装到轴承里了,事先套上一个直线轴承,然后装上传说中的无敌风火轮:

安装轮子

安装轮子

在轮子的另一面,用一个锥型齿轮,把动配合变成静配合,再用螺丝拧紧固定:

锥齿轮固定

锥齿轮固定

最后把打磨用的直流电机拆下来,用联轴器连上。把上面的工序一式两份,底盘小车的两个驱动轮就完成了。明天的计划用铝条和角铝把两个轮子连上,然后再添加两个万向轮制作成底盘。两个直流电机的功率还没有试过,希望能带的动二十公斤的重量。

做好的轮子

做好的轮子

新年到啦!

祝大家新年虎虎生威,龙腾虎跃!
也祝自己和家人新年一切顺利 :)
欢迎大家常回来看看

Arduino开发板实验三:舵机控制

经常玩NXT的朋友肯定对NXT的电机印象深刻,使用非常方便。转速,角度和方向都可以随意控制。

在计划制作小爱的时候,我也一直希望能找到这样的电机。查了一些资料,觉得比较容易控制的有舵机和步进电机。舵机的主要玩家是船模和航模的爱好者们。大海航行靠舵手,舵机就是用来控制舵角的,它的最大特点是可以方便的控制角度,它的限制是舵角一般不超过180度。步进电机就强多了,既可以控制角度,还可以连续旋转,它的问题是输出就是一个光轴,需要加工配套的零件才能使用,另外价格好像会更贵一点。

我计划用舵机来实现小爱的机械臂,最主要的考虑原因是舵机自带很多舵角之类的配件,比较容易安装,毕竟机械加工是制作过程中最麻烦的部分。另外,一般机械臂的关节转角也不需要大于180度,用舵机就足够了。下面是使用Arduino开发板控制舵机的一个小实验。

先抄一段说明:舵机,又称伺服马达,是一种具有闭环控制系统的机电结构。舵机主要是由外壳、电路板、无核心马达、齿轮与位置检测器所构成。其工作原理是由控制器发出PWM(脉冲宽度调制)信号给舵机,经电路板上的IC处理后计算出转动方向,再驱动无核心马达转动,透过减速齿轮将动力传至摆臂,同时由位置检测器(电位器)返回位置信号,判断是否已经到达设定位置,一般舵机只能旋转180度。

舵机结构图

舵机结构图

舵机有3根线,棕色为地,红色为电源正,橙色为信号线,但不同牌子的舵机,线的颜色可能不同,请大家注意。

舵机的转动的角度是通过调节PWM(脉冲宽度调制)信号的占空比来实现的,标准PWM(脉冲宽度调制)信号的周期固定为20ms(50Hz),理论上脉宽分布应在1ms到2ms之间,但是,事实上脉宽可由0.5ms到2.5ms之间,脉宽和舵机的转角0°~180°相对应。有一点值得注意的地方,由于舵机牌子不同,对于同一信号,不同牌子的舵机旋转的角度也会有所不同。

舵机角度和占空比的关系

舵机角度和占空比的关系

 下面这个小实验的目标是用电位器控制舵机的角度,正好在前一个实验里,电位器都还没有拆掉。需要特别注意的是供电部分,舵机转动时电流会比较大,Arduino上的电源芯片可能会因过流保护到发热而损坏,电源需要接到外部供电,切不可使用USB供电。
舵机的棕色线接GND,红色线接VIN(我猜这个是直接连到外接电源的正极),黄色是数据线,接在PWM的7号管脚。电位器的连接稍微有点变换,因为管脚们施展不开,我把正极连在3.3V的接口上,这样模拟输入的范围就变成了0到660左右。前面说了,舵机分别用0.5ms到2.5ms之间的脉冲来对应0到180度左右的角度,我们可以用pulsewidth=(angle*11)+500这样的公式,把0到180度的转角映射到500到2480的脉冲时间。

接线图

接线图

下面看代码:

int readPin = 6;   //用来连接电位器
int servopin = 7;    //定义舵机接口数字接口7

void servopulse(int angle)//定义一个脉冲函数
{
  int pulsewidth=(angle*11)+500;  //将角度转化为500-2480的脉宽值
  digitalWrite(servopin,HIGH);    //将舵机接口电平至高
  delayMicroseconds(pulsewidth);  //延时脉宽值的微秒数
  digitalWrite(servopin,LOW);     //将舵机接口电平至低
  delayMicroseconds(20000-pulsewidth);
}

void setup()
{
   pinMode(servopin,OUTPUT);//设定舵机接口为输出接口
}

void loop()
{
  //读取电位器(传感器)的读数,接到3.3V,值范围从0到660左右
  int readValue = analogRead(readPin);
  //把值的范围映射到0到165左右
  int angle = readValue / 4;
  //发送50个脉冲
  for(int i=0;i<50;i++)
  {
     //引用脉冲函数
     servopulse(angle);
  }
}

实验结果:当旋转电位器的时候,舵机的角度随之改变。不过最终转角并没有达到180度,在某些范围内,电位器旋转时,舵机没有转动。网上舵机的说明也提到了这点,识别的角度范围是有限的。具体的有效角度范围,我还没有测量,等将来开始制作的时候再说。

北京下雪啦

今天北京又下雪啦,早上先演习了一场小雪。白天趁着天晴,买了无数水果,然后去孕妇学校上课。老天挺给面子,忙完一天以后,又开始下起来了。

到北方十多年了,每次下雪还是非常兴奋,心情也会非常好。今天的雪没有元旦那次大,但是下雪以后非常湿润,空气也非常清新。对于干燥和污染严重的北京来说,简直太难得了。

记得来北京的第一年,第一次下雪的时候,我和另一个南方的哥儿们异常兴奋,在北航的绿园里上串下跳。那时候绿园的中间有一个荷花池,我们两个南方土著用各种石头砸荷花池的冰面。当时的冰面非常结实,我们尝试了鹅卵石,板砖和大石块以后,终于从绿园搬了一条石凳(那种双人恋爱专用型),从荷花池的小桥上扔了下去!这时候突然冒出一个老太太,应该是退休教师,远远的就喊“你们两个干什么,别跑,站住……”。还好我们有体力和智力上的优势。为了不让老太太知道我们是那个宿舍楼的,我们特地向反方向逃窜,绕了一个大圈回到宿舍,然后换了一身衣服,鬼鬼祟祟的回绿园查看情况。

这件事情说明了:一百公斤的石块从4米高落下,依然无法击穿冰层 :)

掐指一算,这已经是14年前的恶性破坏事件了。我在北航住过的12号宿舍楼早就拆除重建,绿园也翻新了(不然夏天透过池水还可以看见那个巨大的石条)。不过很多事情依然像刚过去一样,想起来还是会哈哈大笑,同时也会感慨时间过的真快!

今天的雪太小,没法堆雪人,发一张元旦堆的:

雪人

雪人

解魔方的机器人攻略22 – 蓝牙通讯

前面提到了分辨颜色的三部曲,今天给大家介绍一下NXT和电脑之间的蓝牙通讯。其中在NXT端使用的是Lejos自带的Bluetooth类,在PC端使用的开发工具是VS2008,使用的语言是c#。

有些人鄙视这种连接PC的做法,在他们的眼里,连接了PC以后,乐高就变成了一个遥控玩具。其实对编程开发来说,用Java还是用c#并没有本质的区别。魔方的算法也可以写成Java的版本,无奈的是NXT的内存不足,只能把这种体力活交给电脑了。

1. 蓝牙配对

正所谓千里姻缘一线牵,首先我们要给NXT和PC安排一个相亲大会。NXT已经内置了蓝牙模块,要把它设置成打开并且可见的状态。设置方法请看Lejos的中文教程“蓝牙菜单”。现在很多笔记本也自带了蓝牙模块,如果没有的话,必须买一个蓝牙适配器。注意WinXP开始就都已经自带蓝牙驱动了,如果你的电脑安装了第三方的蓝牙驱动,最好先删除。

蓝牙适配器

蓝牙适配器

准备好定情信物以后,就该安排PC和NXT见面了。PC比较主动,由他开负责寻找:

控制面板中旋转“添加新的蓝牙设备”,可以找到当前可见的NXT

控制面板中旋转“添加新的蓝牙设备”,可以找到当前可见的NXT

找到NXT后,两人会羞答答的先来个握手协议,接下来是交换电话号码。Lejos设置的蓝牙连接密码是1234

输入蓝牙连接密码

输入蓝牙连接密码

你看他们一个是能力超强,名车豪宅,另一个能歌善舞,秀色可餐。简直就是一拍即合啊。到此牵线完毕,以后他们就可以直接通讯了。我们查看一下电脑上的NXT属性,可以看到有个带“DevB”的端口,这个相当于是他们之间的私人电话,记下来后面会用到。

注意看端口号

注意看端口号

2. C#中使用蓝牙通讯

其实配对以后,蓝牙就被模拟成了一个端口,我们可以用最简单的端口通讯来收发信息。首先,在每次启动时,需要连接端口:

BluetoothConnection = new SerialPort();
ConnectButton.Enabled = false;
BluetoothConnection.PortName = PortList.SelectedItem.ToString();
BluetoothConnection.Open();
BluetoothConnection.ReadTimeout = 10000;
BluetoothConnection.DataReceived += new SerialDataReceivedEventHandler(BlueToothDataReceived);

然后可以通过这个端口来发送信息。需要注意的是,在发送的原始数据之前,需要添加两个表示长度的字节,Byte[0]+Byte[1]*255=length。所以发送数据的函数如下:

private void BlueToothDataSend(byte[] data)
{
    int length = data.Length;
    byte[] readData = new byte[length + 2];
    readData[0] = (byte)(length % 255);
    readData[1] = (byte)(length / 255);
    for (int i = 0; i < length; i++)
    {
        readData[i + 2] = data[i];
    }
    BluetoothConnection.Write(readData, 0, length + 2);
    Status = "发送数据字节数:" + length;
}

收到数据的时候,也是类似的情况,头两个字节表示了数据的长度,然后才是真正的数据内容:

private void BlueToothDataReceived(object o, SerialDataReceivedEventArgs e)
{
    int length = BluetoothConnection.ReadByte();
    length += BluetoothConnection.ReadByte() * 256;

    byte[] data = new byte[length];
    BluetoothConnection.Read(data, 0, length);
    for (int i = 0; i < length; i++)
    {
        BlueToothReceivedData += string.Format("data[{0}] = {1}\r\n", i, data[i]);
    }
}

断开蓝牙连接的命令如下:

BluetoothConnection.Close();
BluetoothConnection.Dispose();
BluetoothConnection = null;

3. Lejos中使用蓝牙通讯

在Lejos中使用蓝牙有几点区别:首先,Lejos中不支持收到消息的事件触发(我怀疑用多线程可以实现,不过对Java不太熟悉,没有调试成功)所以在需要接受PC信息时,只能挂起等候消息传来;其次,虽然PC发来的信息头两个字节表示长度,但是Lejos接收时,是从第三个字节开始显示的;另外,Lejos发送蓝牙信息时,不需要添加那两个字节的长度信息。

下面是建立蓝牙连接的方式:

public static void Connect() throws Exception
{
	LCD.clear();
	LCD.drawString("Waiting BTC...",0,0);
	btc = Bluetooth.waitForConnection();
	LCD.drawString("Connected",0,2);
	LCD.refresh();
	dis = btc.openDataInputStream();
	dos = btc.openDataOutputStream();
}

接受蓝牙信息:

public static byte[] ReadBytes() throws Exception
{
  byte[] buffer = new byte[255];
  int length = btc.read(buffer, buffer.length);
  if(length==-2)
  {
   //lost data, re-sync
   btc.read(null, 255);
   return new byte[0];
  }
  else
  {
   byte[] data = new byte[length];
   for(int i=0;i<length;i++)
   {
    data[i] = buffer[i];
   }
   return data;
  }
}

发送蓝牙信息

public static void WriteBytes(byte[] data) throws Exception
{
 for(int i=0;i<data.length;i++)
 {
  dos.writeByte(data[i]);
 }
 dos.flush();
}

关闭蓝牙连接

public static void Disconnect() throws Exception
{
   if(btc!=null)
   {
    WriteBytes(new byte[]{(byte)255,(byte)255,(byte)255});
    Thread.sleep(100);
    dos.close();
    dis.close();
    btc.close();
   }
}

4. 蓝牙通讯小实验

下面进行一个小实验,在PC上运行一个程序。
当发送1时,NXT初始化魔方底盘位置;
当发送2时,NXT初始化颜色传感器位置;
当发送3时,NXT读取颜色信息,并回传给电脑;
当发送其他数字时,NXT断开蓝牙连接,并退出程序

蓝牙连接通讯实验

蓝牙连接通讯实验

大部分函数在前面都介绍过了,只需要在main函数中指定操作即可:

BlueTooth.Connect();
byte[] colorData = new byte[6];

while(true)
{
 byte[] readData = BlueTooth.ReadBytes();
 if(readData.length > 0)
 {
  int action = readData[0];
   switch(action)
  {
  case 1:
   Robot.FixBasePosition();
   break;
  case 2:
   Robot.FixColorSensorPosition();
   break;
  case 3:
      colorData[0] = (byte) color.getRed();
      colorData[1] = (byte) color.getGreen();
      colorData[2] = (byte) color.getBlue();
      colorData[3] = (byte) (color.getRawRed() / 3);
      colorData[4] = (byte) (color.getRawGreen() / 3);
      colorData[5] = (byte) (color.getRawBlue() / 3);
      BlueTooth.WriteBytes(colorData);
      break;
  default:
   BlueTooth.Disconnect();
   return;
  }
 }
 Thread.sleep(1000);
}

好了,其余部分自己看代码吧,搭车赠送一个生成三维魔方图形的小程序。点此查看运行在NXT中Java源代码代码;点此下载运行在电脑上的C#程序源代码。