$ influx
Connected to http://localhost:8086 version 1.8.10
InfluxDB shell version: 1.8.10
> show databases
name: databases
name
----
_internal
iot01
munin
> use iot01
Using database iot01
> show tag keys
name: ESP32
tagKey
------
device
name: measurements
tagKey
------
device
name: pi2B
tagKey
------
host
place
.........
.........
>DROP SERIES FROM ESP32
「電子工作」カテゴリーアーカイブ
atom pico
https://qiita.com/Ninagawa123/items/df3930cdecb2e1500f96
STAMP PICO USB-SERIAL
3v3 3v3/Vcc※1
Tx0 RX
Rx0 TX
EN DTR(RTSと接続が正解?)
0 GND※2
GND GND
※2 書き込み時はSTAMP PICOの0と書いてあるピンをGNDに接地させる。
#define BUTTON_PIN 39 // atom nano の内蔵PINは39
#define PIXEL_PIN 27 // Digital IO pin connected to the NeoPixels.
#define PIXEL_COUNT 16 // Number of NeoPixels
ESP32 TTGO-T7の情報
Weather StationのWiFi化
- センサーの方式確認
- 風向 2個のホール素子
- 風速 回転するとON/OFFするスイッチ
- 雨量 調査中(ベランダの庇があって正確な測定は困難なので、必須としない)
- 気温と湿度は、BME280に置き換える
- コントローラ(CPU)
- ESP8266
- データ転送
- influxDB clientと、ESP8266のWiFi機能を利用
jetson nanoでcv2(cuda有効化)
ソースからインストール
python3でcv2をimportすると crashしてコアーダンプ。
問題の解決:export OPENBLAS_CORETYPE=ARMV8
こちらを参照して解決。Jetson NanoのPython3環境でIllegal instruction (cpre dumped)
処理速度の比較測定:約3倍
$ sudo nvpmodel -m 0
$ sudo jetson_clocks
$ python3 opencv_cuda.py
CPU = 2.7655137538909913[msec]
GPU = 1.0501614570617677[msec]
1
$ python3 opencv_cuda.py
CPU = 2.7816075325012206[msec]
GPU = 0.9869620561599731[msec]
1
opencv_cuda.py
import sys
import time
import cv2
### VALUES
NUM_REPEAT = 10000
### Read source image
img_src = cv2.imread("resource/lena.jpg")
cv2.imshow('img_src', img_src)
### Run with CPU
time_start = time.time()
for i in range (NUM_REPEAT):
img_dst = cv2.resize(img_src, (300, 300))
time_end = time.time()
print ("CPU = {0}".format((time_end - time_start) * 1000 / NUM_REPEAT) + "[msec]")
cv2.imshow('CPU', img_dst)
### Run with GPU
img_gpu_src = cv2.cuda_GpuMat() # Allocate device memory only once, as memory allocation seems to take time...
img_gpu_dst = cv2.cuda_GpuMat()
time_start = time.time()
for i in range (NUM_REPEAT):
img_gpu_src.upload(img_src)
img_gpu_dst = cv2.cuda.resize(img_gpu_src, (300, 300))
img_dst = img_gpu_dst.download()
time_end = time.time()
print ("GPU = {0}".format((time_end - time_start) * 1000 / NUM_REPEAT) + "[msec]")
cv2.imshow('GPU', img_dst)
key = cv2.waitKey(0)
cv2.destroyAllWindows()
print(cv2.cuda.getCudaEnabledDeviceCount())
cudaで利用できる機能を表示してみる
import cv2
cv2.__version__
dir(cv2.cuda)
['ALPHA_ATOP', 'ALPHA_ATOP_PREMUL', 'ALPHA_IN', 'ALPHA_IN_PREMUL', 'ALPHA_OUT', 'ALPHA_OUT_PREMUL', 'ALPHA_OVER', 'ALPHA_OVER_PREMUL', 'ALPHA_PLUS', 'ALPHA_PLUS_PREMUL', 'ALPHA_PREMUL', 'ALPHA_XOR', 'ALPHA_XOR_PREMUL', 'BroxOpticalFlow_create', 'COLOR_BAYER_BG2BGR_MHT', 'COLOR_BAYER_BG2GRAY_MHT', 'COLOR_BAYER_BG2RGB_MHT', 'COLOR_BAYER_GB2BGR_MHT', 'COLOR_BAYER_GB2GRAY_MHT', 'COLOR_BAYER_GB2RGB_MHT', 'COLOR_BAYER_GR2BGR_MHT', 'COLOR_BAYER_GR2GRAY_MHT', 'COLOR_BAYER_GR2RGB_MHT', 'COLOR_BAYER_RG2BGR_MHT', 'COLOR_BAYER_RG2GRAY_MHT', 'COLOR_BAYER_RG2RGB_MHT', 'COLOR_BayerBG2BGR_MHT', 'COLOR_BayerBG2GRAY_MHT', 'COLOR_BayerBG2RGB_MHT', 'COLOR_BayerGB2BGR_MHT', 'COLOR_BayerGB2GRAY_MHT', 'COLOR_BayerGB2RGB_MHT', 'COLOR_BayerGR2BGR_MHT', 'COLOR_BayerGR2GRAY_MHT', 'COLOR_BayerGR2RGB_MHT', 'COLOR_BayerRG2BGR_MHT', 'COLOR_BayerRG2GRAY_MHT', 'COLOR_BayerRG2RGB_MHT', 'CascadeClassifier_create', 'DEVICE_INFO_COMPUTE_MODE_DEFAULT', 'DEVICE_INFO_COMPUTE_MODE_EXCLUSIVE', 'DEVICE_INFO_COMPUTE_MODE_EXCLUSIVE_PROCESS', 'DEVICE_INFO_COMPUTE_MODE_PROHIBITED', 'DYNAMIC_PARALLELISM', 'DensePyrLKOpticalFlow_create', 'DescriptorMatcher_createBFMatcher', 'DeviceInfo_ComputeModeDefault', 'DeviceInfo_ComputeModeExclusive', 'DeviceInfo_ComputeModeExclusiveProcess', 'DeviceInfo_ComputeModeProhibited', 'EVENT_BLOCKING_SYNC', 'EVENT_DEFAULT', 'EVENT_DISABLE_TIMING', 'EVENT_INTERPROCESS', 'Event_BLOCKING_SYNC', 'Event_DEFAULT', 'Event_DISABLE_TIMING', 'Event_INTERPROCESS', 'Event_elapsedTime', 'FEATURE_SET_COMPUTE_10', 'FEATURE_SET_COMPUTE_11', 'FEATURE_SET_COMPUTE_12', 'FEATURE_SET_COMPUTE_13', 'FEATURE_SET_COMPUTE_20', 'FEATURE_SET_COMPUTE_21', 'FEATURE_SET_COMPUTE_30', 'FEATURE_SET_COMPUTE_32', 'FEATURE_SET_COMPUTE_35', 'FEATURE_SET_COMPUTE_50', 'FarnebackOpticalFlow_create', 'FastFeatureDetector_create', 'GLOBAL_ATOMICS', 'GpuMat_defaultAllocator', 'GpuMat_setDefaultAllocator', 'HOG_create', 'HOST_MEM_PAGE_LOCKED', 'HOST_MEM_SHARED', 'HOST_MEM_WRITE_COMBINED', 'HostMem_PAGE_LOCKED', 'HostMem_SHARED', 'HostMem_WRITE_COMBINED', 'NATIVE_DOUBLE', 'NVIDIA_OPTICAL_FLOW_1_0_NV_OF_PERF_LEVEL_FAST', 'NVIDIA_OPTICAL_FLOW_1_0_NV_OF_PERF_LEVEL_MAX', 'NVIDIA_OPTICAL_FLOW_1_0_NV_OF_PERF_LEVEL_MEDIUM', 'NVIDIA_OPTICAL_FLOW_1_0_NV_OF_PERF_LEVEL_SLOW', 'NVIDIA_OPTICAL_FLOW_1_0_NV_OF_PERF_LEVEL_UNDEFINED', 'NvidiaOpticalFlow_1_0_NV_OF_PERF_LEVEL_FAST', 'NvidiaOpticalFlow_1_0_NV_OF_PERF_LEVEL_MAX', 'NvidiaOpticalFlow_1_0_NV_OF_PERF_LEVEL_MEDIUM', 'NvidiaOpticalFlow_1_0_NV_OF_PERF_LEVEL_SLOW', 'NvidiaOpticalFlow_1_0_NV_OF_PERF_LEVEL_UNDEFINED', 'NvidiaOpticalFlow_1_0_create', 'ORB_create', 'OpticalFlowDual_TVL1_create', 'SHARED_ATOMICS', 'SURF_CUDA_ANGLE_ROW', 'SURF_CUDA_HESSIAN_ROW', 'SURF_CUDA_LAPLACIAN_ROW', 'SURF_CUDA_OCTAVE_ROW', 'SURF_CUDA_ROWS_COUNT', 'SURF_CUDA_SIZE_ROW', 'SURF_CUDA_X_ROW', 'SURF_CUDA_Y_ROW', 'SparsePyrLKOpticalFlow_create', 'StereoBeliefPropagation_estimateRecommendedParams', 'StereoConstantSpaceBP_estimateRecommendedParams', 'Stream_Null', 'TargetArchs_has', 'TargetArchs_hasBin', 'TargetArchs_hasEqualOrGreater', 'TargetArchs_hasEqualOrGreaterBin', 'TargetArchs_hasEqualOrGreaterPtx', 'TargetArchs_hasEqualOrLessPtx', 'TargetArchs_hasPtx', 'WARP_SHUFFLE_FUNCTIONS', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'absSum', 'absdiff', 'add', 'addWeighted', 'alphaComp', 'bilateralFilter', 'bitwise_and', 'bitwise_not', 'bitwise_or', 'bitwise_xor', 'blendLinear', 'buildWarpAffineMaps', 'buildWarpPerspectiveMaps', 'calcAbsSum', 'calcHist', 'calcNorm', 'calcNormDiff', 'calcSqrSum', 'calcSum', 'cartToPolar', 'compare', 'copyMakeBorder', 'countNonZero', 'createBackgroundSubtractorMOG', 'createBackgroundSubtractorMOG2', 'createBoxFilter', 'createBoxMaxFilter', 'createBoxMinFilter', 'createCLAHE', 'createCannyEdgeDetector', 'createColumnSumFilter', 'createContinuous', 'createConvolution', 'createDFT', 'createDerivFilter', 'createDisparityBilateralFilter', 'createGaussianFilter', 'createGeneralizedHoughBallard', 'createGeneralizedHoughGuil', 'createGoodFeaturesToTrackDetector', 'createHarrisCorner', 'createHoughCirclesDetector', 'createHoughLinesDetector', 'createHoughSegmentDetector', 'createLaplacianFilter', 'createLinearFilter', 'createLookUpTable', 'createMedianFilter', 'createMinEigenValCorner', 'createMorphologyFilter', 'createRowSumFilter', 'createScharrFilter', 'createSeparableLinearFilter', 'createSobelFilter', 'createStereoBM', 'createStereoBeliefPropagation', 'createStereoConstantSpaceBP', 'createTemplateMatching', 'cvtColor', 'demosaicing', 'dft', 'divide', 'drawColorDisp', 'ensureSizeIsEnough', 'equalizeHist', 'evenLevels', 'exp', 'findMinMax', 'findMinMaxLoc', 'flip', 'gammaCorrection', 'gemm', 'getCudaEnabledDeviceCount', 'getDevice', 'histEven', 'histRange', 'integral', 'log', 'magnitude', 'magnitudeSqr', 'max', 'meanShiftFiltering', 'meanShiftProc', 'meanShiftSegmentation', 'meanStdDev', 'merge', 'min', 'minMax', 'minMaxLoc', 'mulAndScaleSpectrums', 'mulSpectrums', 'multiply', 'norm', 'normalize', 'phase', 'polarToCart', 'pow', 'printCudaDeviceInfo', 'printShortCudaDeviceInfo', 'pyrDown', 'pyrUp', 'rectStdDev', 'reduce', 'registerPageLocked', 'remap', 'reprojectImageTo3D', 'resetDevice', 'resize', 'rotate', 'setBufferPoolConfig', 'setBufferPoolUsage', 'setDevice', 'split', 'sqr', 'sqrIntegral', 'sqrSum', 'sqrt', 'subtract', 'sum', 'threshold', 'transpose', 'unregisterPageLocked', 'warpAffine', 'warpPerspective']
M-JPEG streamerとOpenCV(CV2)でタイムラプス動画を作成
10秒間隔で画像を400枚 jpgファイルとして保存
事前にM-JPEG streamerがインストールされ、アドレスxxx.xxx.xxx.xxx:8080で稼働している環境で、ファイル名 T0000.jpgからT0399.jpgとして画像を保存します。
import time
import cv2
# VideoCapture オブジェクトを取得します
URL = "http://xxx.xxx.xxx.xxx:8080/?action=stream"
capture = cv2.VideoCapture(URL)
for n in range(400):
ret, frame = capture.read()
name = "T" + '{:04d}'.format(n)+".jpg"
print(name)
cv2.imwrite(name, frame)
time.sleep(10)
print("Done!")
jpgファイルからmp4動画を生成
import glob
import cv2
img_array = []
for filename in sorted(glob.glob("*.jpg")):
print(filename)
img = cv2.imread(filename)
height, width, layers = img.shape
size = (width, height)
img_array.append(img)
name = 'project.mp4'
out = cv2.VideoWriter(name, cv2.VideoWriter_fourcc(*'mp4v'), 5.0, size)
for i in range(len(img_array)):
out.write(img_array[i])
out.release()
生成したmp4動画の編集(不要部分の削除、回転、再生速度の調整など)には、Windows10標準のソフト「フォト」が便利。
作例
Raspberry Pi3BでRepetier-Serverを利用する
Raspberry Pi3Bの環境
$ uname -a
Linux ps2 5.10.52-v7+ #1440 SMP Tue Jul 27 09:54:13 BST 2021 armv7l GNU/Linux
$ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
ダウンロードサイトからLinux 32bit ARM(armfh)をダウンロードしてインストール
$ sudo dpkg -i Repetier-Server-1.1.2-Linux.deb
インストールしたら、ブラウザで http://xxx.xxx.xxx.xxx:3344 をアクセスすると、プリンターの設定画面が表示されるので、マニュアルに従ってプリンターの追加・設定を行います。
Webcam のインストール
Repetier-ServerでWebcamの機能を利用するには、有料のプロバージョンが必要なため、 次のような画面となって、サーバーでは表示できません。
ただし、インストールしたWebcamのストリーミング機能は動作しているようで、ブラウザーで http://xxx.xxx.xxx.xxx:8080 をアクセスすると次のようにストリーミング映像を見ることができました。(カメラは窓の外向けに設置)
Start, Stop and Restart Repetier-Server
# Start server
sudo service RepetierServer start
sudo /etc/init.d/RepetierServer start
# Stop server
sudo service RepetierServer stop
sudo /etc/init.d/RepetierServer stop
# Restart server
sudo service RepetierServer stop
sudo /etc/init.d/RepetierServer restart
streamlitを試す
streamlitはpythonコードだけで、Webブラウザーからアクセスできるアプリを公開できる優れものです。磁気センサーのオフセット可視化で試してみました。jupyterのコードへ多少手を加えるだけでOK。デバッグはjupyterで動作確認を行い、完成したら必要に応じてstreamlit化するのが良さそう。streamlitはコマンドラインから次のように実行し、表示されたurlをブラウザーでアクセスします。
$ treamlit run mag-offset.py
You can now view your Streamlit app in your browser.
Network URL: http://192.168.68.122:8501
External URL: http://203.165.226.42:8501
ブラウザーでアクセスした様子
mag-offset.py
import matplotlib
import os
import pandas as pd
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import math
import numpy as np
import streamlit as st
st.title('Magnetic compass offset plot')
### データの読み込み
df = pd.read_csv('BMX055/data4.csv')
#print(df)
a_x=np.average(df['Mag_x'])
a_y=np.average(df['Mag_y'])
a_z=np.average(df['Mag_z'])
#print(round(a_x,2),round(a_y,2),round(a_z,2))
# ここからグラフ描画
# グラフの入れ物を用意する。
fig = plt.figure()
#ax = Axes3D(fig) <--- warningとなるので、次の行に書き換え
ax = fig.add_subplot(111, projection='3d')
# 軸のラベルを設定する。
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
st.table(pd.DataFrame({
'Center X': [a_x],
'Center Y': [a_y],
'Center Z': [a_z]
}))
# グラフを表示する。
ax.scatter3D(df['Mag_x'],df['Mag_y'],df['Mag_z'],color="blue")
ax.scatter3D(a_x,a_y,a_z,color="red")
#plt.show() <--- plt.show()を st.write(fig)へ置き換える。
st.write(fig)
ESP32/DCモータ/Websocket UIで倒立振子
PID制御のパラメータと自立のための目標角度(TARGET)をWebsocket UIでスマホから設定します。今のところ、パラメータの値が適切でないようで、硬い床面上では短時間で倒れます。やわらかい素材の上では倒れないので、この状態で適切なパラメータを試行錯誤で探索中、、、WebベースのUIのためか、細かなパラメータ設定の操作性がいまいちなので、改善が必要かもしれない。
倒立振子のコードは、https://qiita.com/Google_Homer/items/3897e7ffef9d247e2f56 を参考にしています。
Websocketのコードは、https://github.com/mgo-tec/ESP32_SPIFFS_EasyWebSocket を参考にしています。
#include <WiFi.h>
#include <WiFiMulti.h>
#include "ESP32_SPIFFS_EasyWebSocket.h" //beta ver 1.60
#include <MadgwickAHRS.h>
Madgwick MadgwickFilter;
#include "I2Cdev.h"
#define MPU6050_PWR_MGMT_1 0x6B
#define MPU_ADDRESS 0x68
#include "Wire.h"
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
//PIDパラメータ調整
//PID係数
//#define TARGET 0.4
//#define KP 100.0
//#define KI 2.0
//#define KD 2.0
//Motor
#define LED_PIN 27 // 内蔵LED
#define MOTOR_PIN_F A4 // to DC Motor Driver FIN
#define MOTOR_PIN_R A18 // to DC Motor Driver RIN
#define MOTOR_PWM_F 0 // PWM CHANNEL
#define MOTOR_PWM_R 1 // PWM CHANNEL
#define MOTOR_POWER_MIN 50
int MOTOR_POWER_MAX = 200;
#define DPS 1000 // Gscale [deg/s]
const char* ssid = "*******"; //ご自分のルーターのSSIDに書き換えてください
const char* password = "******"; //ご自分のルーターのパスワードに書き換えてください
const char* HTM_head_file1 = "/EWS/LIPhead1.txt"; //HTMLヘッダファイル1
const char* HTM_head_file2 = "/EWS/LIPhead2.txt"; //HTMLヘッダファイル2
const char* HTML_body_file = "/EWS/dummy.txt"; //HTML body要素ファイル(ここではダミーファイルとしておく)
const char* dummy_file = "/EWS/dummy.txt"; //HTMLファイル連結のためのダミーファイル
ESP32_SPIFFS_EasyWebSocket ews;
WiFiMulti wifiMulti;
IPAddress LIP; //ローカルIPアドレス自動取得用
String ret_str; //ブラウザから送られてくる文字列格納用
String txt = "text send?"; //ブラウザから受信した文字列を ESP32から再送信する文字列
int PingSendTime = 10000; //ESP32からブラウザへPing送信する間隔(ms)
long ESP32_send_LastTime;
int ESP32_send_Rate = 300;
byte cnt = 0;
//PID
float power = 0, I = 0, preP = 0, preTime;
float now = 0, Duty = 0, pitch, roll, yaw;
float KP = 100.0, KI = 2.0, KD = 2.0, TARGET = 0.0;
boolean f_disp = false;
void setup() {
Wire.begin();
Serial.begin(38400);
Wire.beginTransmission(MPU_ADDRESS);
Wire.write(MPU6050_PWR_MGMT_1); //MPU6050_PWR_MGMT_1レジスタの設定
Wire.write(0x00);
Wire.endTransmission();
Serial.print(F("Connecting to "));
Serial.println(ssid);
wifiMulti.addAP(ssid, password);
Serial.println(F("Connecting Wifi..."));
if (wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println(F("WiFi connected"));
Serial.println(F("IP address: "));
LIP = WiFi.localIP(); //ESP32のローカルIPアドレスを自動取得
Serial.println(WiFi.localIP());
}
ews.EWS_server_begin();
Serial.println(); Serial.println("Initializing SPIFFS ...");
if (!SPIFFS.begin()) {
Serial.println("SPIFFS failed, or not present");
return;
}
Serial.println("SPIFFS initialized. OK!");
MadgwickFilter.begin(100); //100Hz
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
pinMode(MOTOR_PIN_F, OUTPUT);
pinMode(MOTOR_PIN_R, OUTPUT);
ledcSetup(MOTOR_PWM_F, 312500, 8); //CHANNEL, FREQ, BIT
ledcSetup(MOTOR_PWM_R, 312500, 8);
ledcAttachPin(MOTOR_PIN_F, MOTOR_PWM_F);
ledcAttachPin(MOTOR_PIN_R, MOTOR_PWM_R);
// 初期化
delay(10);
// Clear the buffer
display.clearDisplay();
// テキストサイズを設定
display.setTextSize(1);
// テキスト色を設定
display.setTextColor(WHITE);
display.setCursor(20, 5);
display.println("Start...");
display.setCursor(20, 25);
display.println(WiFi.localIP());
display.display();
delay(10); // Pause for 2 seconds
preTime = micros();
// TaskHandle_t th; //ESP32 マルチタスク ハンドル定義
// xTaskCreatePinnedToCore(Task1, "Task1", 4096, NULL, 5, &th, 0); //マルチタスク core 0 実行
ESP32_send_LastTime = millis();
}
void loop() {
websocket_handshake();
if (ret_str != "_close") {
if (millis() - ESP32_send_LastTime > ESP32_send_Rate) {
if (cnt > 3) {
cnt = 0;
}
websocket_send(cnt, txt);
cnt++;
ESP32_send_LastTime = millis();
}
ret_str = ews.EWS_ESP32CharReceive(PingSendTime);
if (ret_str != "\0") {
Serial.println(ret_str);
if (ret_str != "Ping") {
if (ret_str[0] != 't') {
int ws_data = (ret_str[0] - 0x30) * 100 + (ret_str[1] - 0x30) * 10 + (ret_str[2] - 0x30);
switch (ret_str[4]) {
case '!':
ESP32_send_Rate = ws_data;
break;
case 'B':
TARGET = float(5.0 - ws_data / 20.0);
break;
case 'G':
KP = float(ws_data / 1.8) ;
break;
case 'R':
KI = float(ws_data / 50.0);
break;
case '_':
KD = float(ws_data / 50.0);
break;
case 'A':
f_disp = ! f_disp;
break;
case 'O':
KP = 100, KI = 2.0, KD = 2.0, TARGET = 0.0;
break;
}
} else if (ret_str[0] == 't') {
txt = ret_str.substring(ret_str.indexOf('|') + 1, ret_str.length() - 1);
Serial.println(txt);
}
}
}
} else if (ret_str == "_close") {
ESP32_send_LastTime = millis();
ret_str = "";
}
PID();
}
//************* 倒立振り子 ****************************************
void PID() {
float P, D, dt, Time;
static float powerI = 0;
Wire.beginTransmission(0x68);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(0x68, 14, true);
while (Wire.available() < 14);
int16_t axRaw, ayRaw, azRaw, gxRaw, gyRaw, gzRaw, Temperature;
axRaw = Wire.read() << 8 | Wire.read();
ayRaw = Wire.read() << 8 | Wire.read();
azRaw = Wire.read() << 8 | Wire.read();
Temperature = Wire.read() << 8 | Wire.read();
gxRaw = Wire.read() << 8 | Wire.read();
gyRaw = Wire.read() << 8 | Wire.read();
gzRaw = Wire.read() << 8 | Wire.read();
// 加速度値を分解能で割って加速度(G)に変換する
float acc_x = axRaw / 16384.0; //FS_SEL_0 16,384 LSB / g
float acc_y = ayRaw / 16384.0;
float acc_z = azRaw / 16384.0;
// 角速度値を分解能で割って角速度(degrees per sec)に変換する
float gyro_x = gxRaw / 131.0; // (度/s)
float gyro_y = gyRaw / 131.0;
float gyro_z = gzRaw / 131.0;
/*
//c.f. Madgwickフィルターを使わずに、PRY(pitch, roll, yaw)を計算
double roll = atan2(acc_y, acc_z) * RAD_TO_DEG;
double pitch = atan(-acc_x / sqrt(acc_y * acc_y + acc_z * acc_z)) * RAD_TO_DEG;
*/
//Madgwickフィルターを用いて、PRY(pitch, roll, yaw)を計算
MadgwickFilter.updateIMU(gyro_x, gyro_y, gyro_z, acc_x, acc_y, acc_z);
//PRYの計算結果を取得する
roll = MadgwickFilter.getRoll();
pitch = MadgwickFilter.getPitch();
yaw = MadgwickFilter.getYaw();
now = TARGET - roll ; // 目標角度から現在の角度を引いて偏差を求める
if (f_disp) {
display.clearDisplay();
display.setCursor(20, 5);
display.println( roll);
display.setCursor(20, 25);
display.println( now);
display.display();
}
if (-20 < now && now < 20) {
Time = micros() ;
dt = (Time - preTime) / 1000000 ; // 処理時間を求める
preTime = Time ; // 処理時間を記録
P = now / 90 ; // -90~90→-1.0~1.0
I += P * dt ; // 偏差を積分する
D = (P - preP) / dt ; // 偏差を微分する
preP = P ; // 偏差を記録する
power += KP * P + KI * I + KD * D ; // 出力を計算する
if (power < -1) power = -1 ; // →-1.0~1.0
if (1 < power) power = 1 ;
//Motor駆動
Duty = (int)((MOTOR_POWER_MAX - MOTOR_POWER_MIN) * abs(power) + MOTOR_POWER_MIN);
ledcWrite( MOTOR_PWM_F, (power < 0 ? 0 : Duty) );
ledcWrite( MOTOR_PWM_R, (power < 0 ? Duty : 0) );
digitalWrite(LED_PIN, HIGH);
if (f_disp) {
display.clearDisplay();
display.setCursor(20, 5);
display.println( roll);
display.setCursor(20, 25);
display.println( Duty);
display.setCursor(20, 45);
display.println( power);
display.display();
}
} else { // 転倒したら停止
ledcWrite(MOTOR_PWM_F, 0);
ledcWrite(MOTOR_PWM_R, 0);
power = 0;
I = 0;
digitalWrite(LED_PIN, LOW);
}
}
//**************************************************************
void LED_PWM(byte Led_gr, byte channel, int data_i) {
Serial.println(data_i);
}
//*********************************************
void websocket_send(uint8_t count, String str_txt) {
String str, tmp;
//※WebSocketへのテキスト送信は110 byte 程度なので、全角35文字程度に抑えること
tmp = "AGL=" + String(roll) + ",TGT=" + String(TARGET) + ",P=" + String(KP) + ",I=" + String(KI) + ",D=" + String(KD);
switch (cnt) {
case 0:
//str = str_txt;
str = tmp;
break;
case 1:
str = tmp;
break;
case 2:
str = tmp;
break;
case 3:
str = tmp;
break;
}
ews.EWS_ESP32_Str_SEND(str, "wroomTXT"); //ブラウザに文字列を送信
}
//************************* Websocket handshake **************************************
void websocket_handshake() {
if (ews.Get_Http_Req_Status()) { //ブラウザからGETリクエストがあったかどうかの判定
String html_str1 = "", html_str2 = "", html_str3 = "", html_str4 = "", html_str5 = "", html_str6 = "", html_str7 = "";
//※String変数一つにEWS_Canvas_Slider_T関数は2つまでしか入らない
html_str1 += "<body style='background:#000; color:#fff;'>\r\n";
html_str1 += "<font size=3>\r\n";
html_str1 += "ESP-WROOM-32(ESP32)\r\n";
html_str1 += "<br>\r\n";
html_str1 += "ESP32_SPIFFS_EasyWebSocket Beta1.60 Sample\r\n";
html_str1 += "</font><br>\r\n";
html_str1 += ews.EWS_BrowserSendRate();
html_str1 += "<br>\r\n";
html_str1 += ews.EWS_ESP32_SendRate("!esp32t-Rate");
html_str1 += "<br>\r\n";
html_str1 += ews.EWS_BrowserReceiveTextTag2("wroomTXT", "from ESP32 DATA", "#555", 20, "#00FF00");
html_str1 += "<br>\r\n";
html_str1 += ews.EWS_Status_Text2("WebSocket Status", "#555", 20, "#FF00FF");
html_str1 += "<br><br>\r\n";
html_str2 += ews.EWS_TextBox_Send("txt1", "Hello Easy WebSocket Beta1.60", "送信");
html_str2 += "<br><br>\r\n";
html_str2 += "SETTING \r\n";
html_str2 += ews.EWS_On_Momentary_Button("ALL", "ALL-ON", 80, 25, 15, "#000000", "#AAAAAA");
html_str2 += ews.EWS_On_Momentary_Button("OUT", "DEFALT", 80, 25, 15, "#FFFFFF", "#555555");
html_str2 += "<br>\r\n";
html_str3 += "<br>TGT_ :\r\n";
html_str3 += ews.EWS_Canvas_Slider_T("BLUE", 240, 40, "#777777", "#0000ff"); //CanvasスライダーはString文字列に2つまでしか入らない
html_str3 += "<br>PID P:\r\n";
html_str3 += ews.EWS_Canvas_Slider_T("GREEN", 250, 40, "#777777", "#00ff00"); //CanvasスライダーはString文字列に2つまでしか入らない
html_str4 += "<br>PID I:\r\n";
html_str4 += ews.EWS_Canvas_Slider_T("RED", 250, 40, "#777777", "#ff0000"); //CanvasスライダーはString文字列に2つまでしか入らない
html_str4 += "<br>PID D:\r\n";
html_str4 += ews.EWS_Canvas_Slider_T("_RGB", 250, 40, "#777777", "#ffff00");
html_str7 += "<br><br>\r\n";
html_str7 += ews.EWS_WebSocket_Reconnection_Button2("WS-Reconnect", "grey", 200, 40, "black" , 17);
html_str7 += "<br><br>\r\n";
html_str7 += ews.EWS_Close_Button2("WS CLOSE", "#bbb", 150, 40, "red", 17);
html_str7 += ews.EWS_Window_ReLoad_Button2("ReLoad", "#bbb", 150, 40, "blue", 17);
html_str7 += "</body></html>";
//WebSocket ハンドシェイク関数
ews.EWS_HandShake_main(3, HTM_head_file1, HTM_head_file2, HTML_body_file, dummy_file, LIP, html_str1, html_str2, html_str3, html_str4, html_str5, html_str6, html_str7);
}
}
サーボモータを利用したPan/Tiltカメラ台の制御(pigpioの利用)
こんな記事を発見:RPi.GPIOよりもpigpioの方が精度が良いらしい。
「RPi.GPIO と pigpio のパルス幅の精度を測定」
テストのコード
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
import pigpio
PAN=27
GP = pigpio.pi()
GP.set_mode(PAN, pigpio.OUTPUT)
GP.set_servo_pulsewidth(PAN, 500) # gpio18 500us
time.sleep(60)
GP.stop()
実測結果
標準偏差が348nSと、OSのオーバーヘッドがないArduinoと比較すると2桁程大きいが、サーボモータの制御には十分な精度が得られそう、、、