昨日、ふとiPhoneでメールを確認していると、去年度末にYouTubeにアップしたPixyの動画にコメントがついたよ、とメールが・・・
そのままコメントで返すとわかりづらそうな内容だったので記事にしました。

※ 前相方の「コメント返し」をパクったわけではありません。
IMG_7800

コメントをいただいた動画はこの動画です。


コメントの内容は「部活でPixyを使っているが、Pixy公式ライブラリにあるサンプルプログラム「hello_world」のシリアル出力が何を表してるのかわからないから教えて欲しい」とのことでした。

さて、そのプログラムがこちらです。
//
// begin license header
//
// This file is part of Pixy CMUcam5 or "Pixy" for short
//
// All Pixy source code is provided under the terms of the
// GNU General Public License v2 (http://www.gnu.org/licenses/gpl-2.0.html).
// Those wishing to use Pixy source code, software and/or
// technologies under different licensing terms should contact us at
// cmucam@cs.cmu.edu. Such licensing terms are available for
// all portions of the Pixy codebase presented here.
//
// end license header
//
// This sketch is a good place to start if you're just getting started with 
// Pixy and Arduino.  This program simply prints the detected object blocks 
// (including color codes) through the serial console.  It uses the Arduino's 
// ICSP port.  For more information go here:
//
// http://cmucam.org/projects/cmucam5/wiki/Hooking_up_Pixy_to_a_Microcontroller_(like_an_Arduino)
//
// It prints the detected blocks once per second because printing all of the 
// blocks for all 50 frames per second would overwhelm the Arduino's serial port.
//
   
#include <SPI.h>  
#include <Pixy.h>

// This is the main Pixy object 
Pixy pixy;

void setup()
{
  Serial.begin(9600);
  Serial.print("Starting...\n");

  pixy.init();
}

void loop()
{ 
  static int i = 0;
  int j;
  uint16_t blocks;
  char buf[32]; 
  
  // grab blocks!
  blocks = pixy.getBlocks();
  
  // If there are detect blocks, print them!
  if (blocks)
  {
    i++;
    
    // do this (print) every 50 frames because printing every
    // frame would bog down the Arduino
    if (i%50==0)
    {
      sprintf(buf, "Detected %d:\n", blocks);
      Serial.print(buf);
      for (j=0; j<blocks; j++)
      {
        sprintf(buf, "  block %d: ", j);
        Serial.print(buf); 
        pixy.blocks[j].print();
      }
    }
  }  
}

問題の部分は59行目〜65行目(「sprintf」〜「pixy.blocks[j].print();」)にかけての部分だと思われますので、この部分を少し解説しようと思います。

sprintf

これはC言語の関数で、指定した配列に指定した書式で文字列を書き込むための関数です。
上記のプログラムでは
sprintf(buf, "Detected %d:\n", blocks);
などのように使用されています。
1つ目の引数(上記ではbuf)が、文字列を書き込む先の配列、2つ目の引数がひとつ目の引数で指定した変数に書き込む内容になっています。
上記の例では、配列bufに「"Detected %d:\n", blocks」を書き込めという内容になります。

続いて「"Detected %d:\n", blocks」が示す内容ですが、
「Detected」に続いて変数blockの中身を連結し、最後に「:」と改行コードを連結
という内容になります。
引数内にある「%d」は、このあとの,(カンマ)後で指定する変数を整数値で表示を意味します。

したがって、仮に変数block内に数値「1」が入っていたとすれば、「"Detected %d:\n", blocks」で
Detected 1:\n(\nは改行コード)
が生成され、上記の例では配列bufにこの文字列が代入されることとなります。
今回取り上げているプログラムでは、これらのsprintfの直下に
Serial.print(buf);
があるので、sprintfで生成した文字列をシリアルモニタに出力していることになります。
smonitor_sprintf
実際の出力画面で言うところの、この垢枠の部分です。

pixy.blocks[j].print()

おそらくコメントをくださった方は、これが一番謎なんだと思います。
これはPixyライブラリの関数で、ざっくり言えばPixyから受け取ったデータを出力する関数です。
smonitor_blocksprint
この部分ですね。

pixy.blocks[j].print();
最初のpixyは、Pixyライブラリのクラスのインスタンス名で、プログラムの最初の方(30行目)で生成してあります。
Pixy pixy;
これですね。

blocks[j]は、Pixyは認識したオブジェクト(ブロック)の中で何番のブロックの情報を出すかを指定します。
jで指定している値がオブジェクトの番号で、番号は0から始まって認識した数から1引いた数だけ指定できます。
いくつのオブジェクトを認識したかは、
pixy.getBlocks()
の返り値で得ることができます。
認識させるオブジェクトの最大個数は、Pixymonから設定できます


シリアルモニタで得られる情報について

smonitor_1time
これが示す情報の内容ですが、
  • Detected n+1: ・・・オブジェクトをn個検出したよ。(n+1はPixyが認識したオブジェクトの数)
  • block n: ・・・ここから右の情報はn個目のオブジェクトの情報だよ。(nはPixyが認識したオブジェクトの番号)
  • sig: X ・・・このオブジェクトのシグネチャーはXだよ。(Xはシグネチャーの番号)
  • x: XXX ・・・このオブジェクトのx(横)方向の座標はXXXだよ。(XXXは座標の値)
  • y: XXX ・・・このオブジェクトのy(縦)方向の座標はXXXだよ。(XXXは座標の値)
  • width: XXX ・・・このオブジェクトの幅はXXX pxだよ。(XXXはピクセル数)
  • height: XXX ・・・このオブジェクトの高さはXXX pxだよ。(XXXはピクセル数)
シグネーチャー(signature)は、PixymonでPixy色情報を登録した際の識別番号のようなものです。
Arduinoでこの情報を単体で取得するための関数もあります。
その辺は前の記事を参考にしてください。

さいごに

おそらく、このPixy用Arduinoライブラリを書いた人は、プログラマとしてはなかなかな腕の人でしょう。
私はどこかでプログラミングについてどこかで教えてもらったわけではなく、こういうライブラリからひとりで勉強してほぼひとりプログラムを書いているので、この記事で使っている用語とかの用法が間違っているかもしれません。
用語を使って話を進めないからです。
悪しからず・・・