2020年2月2日日曜日

TensorFlow Lite for microのHello worldサンプルをM5StickVで動かす(その2)

前回


前回のブログでは、TensorFlow Lite for microのサンプル(Hello world)をM5StickV向けにビルド、動作させるところまでを書いた。出力はコンソール出力だけで、M5StickVのLCDには何も表示しない。


目的


今回は、M5StickVのLCDに表示を行うところをメインにする。
こんな感じである。




参考



LCDに表示するまでの道のり


LCDに表示するにはHWの制御がかなり必要である。自前でやるのもよいが、今回はsipeedが提供するMaixPyに含まれるライブラリを使用することにする。

前回のプロジェクトに追加・変更する方針とする。




sipeed / MaixPyのMaixPyのSDK


もともと、Micropython 用の環境であるが、SDKと利用して独自のcプロジェクトをビルドすることができる。


ただ、C++はビルドできないため、SDKのみを利用することにした。

components配下のSDKの部分を利用する。以下のフォルダを利用した。
  • boards
  • drivers
  • kendryte_sdk
  • utils


sipeed/MaixPyのSDKをプロジェクトに追加


プロジェクトに追加する。


sipeed/MaixPyのSDKをC++から呼び出せるように変更


sipeed/MaixPyのSDKはC++から呼び出すことを考慮していない箇所が多数ある。このため、使用する部分のヘッダを変更する。

extern "C" の追加し、呼び出し可能とする。

今回は以下のヘッダを修正。

TensorFlow lite for microのソース変更


以下の処理を追加する。
  • main.ccでLCDを含むHWの初期化
  • output_handler.ccで描画の更新


HWの初期化


src/micro/tensorflow/tensorflow/lite/micro/examples/hello_world/main.ccで行う。
  • HWの初期化を行う。
    m5stick_init()
    (BUTTON B長押しの電源OFFもできるようになったりする)
  • setup_lcd()でLCD初期化を行う。

man.ccはこのような感じになる。

/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/micro/examples/hello_world/main_functions.h"
#include "m5stick.h"
#include "lcd.h"
#include "fpioa.h"
#include "global_config.h"
void setup_lcd(uint16_t fillColor, lcd_dir_t lcdDirection)
{
fpioa_set_function(21, static_cast<_fpioa_function>(FUNC_GPIOHS0 + RST_GPIONUM));
fpioa_set_function(20, static_cast<_fpioa_function>(FUNC_GPIOHS0 + DCX_GPIONUM));
fpioa_set_function(22, static_cast<_fpioa_function>(FUNC_SPI0_SS0 + LCD_SPI_SLAVE_SELECT));
fpioa_set_function(19, FUNC_SPI0_SCLK);
fpioa_set_function(18, FUNC_SPI0_D0);
lcd_init(CONFIG_LCD_DEFAULT_FREQ, false, 52, 40, 40, 52, true, CONFIG_LCD_DEFAULT_WIDTH, CONFIG_LCD_DEFAULT_HEIGHT);
lcd_set_direction(lcdDirection);
lcd_clear(fillColor);
}
// This is the default main used on systems that have the standard C entry
// point. Other devices (for example FreeRTOS or ESP32) that have different
// requirements for entry code (like an app_main function) should specialize
// this main.cc file in a target-specific subfolder.
int main(int argc, char* argv[]) {
m5stick_init();
setup_lcd(BLACK, DIR_YX_LRUD);
setup();
while (true) {
loop();
}
}


出力値に応じた点の描画


src/micro/tensorflow/tensorflow/lite/micro/examples/hello_world/output_handler.ccで行う。

STM32F746の実装を参考。LCD出力用の関数をsipeed/MaixPyのSDKを使用する。ただ、元の実装では円を描画しているが、sipeed/MaixPyのSDKには用意されていない。今回は矩形描画で代替えした。

使用した関数は以下。
  • lcd_get_width ... LCDの幅を取得
  • lcd_get_height ... LCDの高さを取得
  • lcd_clear ... LCDのクリア
  • lcd_fill_rectangle ... 矩形を描画

output_handler.ccはこのような感じになる。

/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/micro/examples/hello_world/output_handler.h"
#include "tensorflow/lite/micro/examples/hello_world/constants.h"
#include "lcd.h"
// The colors we'll draw
const uint16_t background_color = BLACK; // Red
const uint16_t foreground_color = RED; // Red
// The size of the dot we'll draw
const int dot_radius = 3;
// Track whether the function has run at least once
bool initialized = false;
// Size of the drawable area
int width;
int height;
// Midpoint of the y axis
int midpoint;
// Pixels per unit of x_value
int x_increment;
void HandleOutput(tflite::ErrorReporter* error_reporter, float x_value,
float y_value) {
// Do this only once
if (!initialized) {
// Calculate the drawable area to avoid drawing off the edges
width = (int)lcd_get_width() - (dot_radius * 2);
height = (int)lcd_get_height() - (dot_radius * 2);
// Calculate the y axis midpoint
midpoint = height / 2;
// Calculate fractional pixels per unit of x_value
x_increment = static_cast<float>(width) / kXrange;
initialized = true;
}
// Log the current X and Y values
error_reporter->Report("x_value: %f, y_value: %f\n", x_value, y_value);
// Clear the previous drawing
lcd_clear(background_color);
// Calculate x position, ensuring the dot is not partially offscreen,
// which causes artifacts and crashes
int x_pos = dot_radius + static_cast<int>(x_value * x_increment);
// Calculate y position, ensuring the dot is not partially offscreen
int y_pos;
if (y_value >= 0) {
// Since the display's y runs from the top down, invert y_value
y_pos = dot_radius + static_cast<int>(midpoint * (1.f - y_value));
} else {
// For any negative y_value, start drawing from the midpoint
y_pos =
dot_radius + midpoint + static_cast<int>(midpoint * (0.f - y_value));
}
// Draw the dot
lcd_fill_rectangle(x_pos - dot_radius, y_pos - dot_radius,
x_pos + dot_radius, y_pos + dot_radius,
foreground_color);
}

最後に


前回、今回とM5StickVでTensorFlow Lite for microのサンプルをビルドするやり方を記載した。M5StickVには6軸センサやカメラがあるので、他のサンプルも行けると思うのでチャレンジしてみたい。



0 件のコメント:

コメントを投稿