前回までに、topicを使ったPublisherとSubscriverのとてもベーシックなプログラムを作り、動かしました。
ここでは、温度を計測するPublisherを作ります。Subscriverはそのまま使います。
環境
- Raspberry Pi 5 8GB
- 追加ボード;NVMe Base for Raspberry Pi 5 (NVMe Base by Pimoroni)
- Crucial クルーシャル P2シリーズ 500GB 3D NAND NVMe PCIe M.2 SSD CT500P2SSD8
- 初期;RaspberryPi OS Desktop 64bit (Debian version: 12 (bookworm) Release date: March 15th 2024)
- 現在;Ubuntu Desktop 24.04LTS(64-bit)
- ROS2 HumbleではなくJazzy
Windows10で、検索窓にcmdと入れ、コマンドプロンプトを起動します。
sshでログインします(第1回参照)。必要ならupdateします。
C:\Users\yoshi>ssh yoshi.local
温度を測るセンサBME280を接続する
I2CデバイスでつないだボッシュのBME280は、I2C経由でデータを読む方法とデバイス・ドライバを登録してiioアプリが読み出した測定データを読む方法があります。
/boot/firmware/READMEに書かれたラズパイのWebサイトに行っても、ラズパイのOS(bookworm)の/boot/firmware/READMEに書かれたセンサ類の情報が見つかりません。ものは試しで、bookwormと同じ手順で組み込んでみました。
i2cdetectが使えるように、i2c-toolsをインストールします。
C:\Users\yoshi>ssh yoshi.local
yoshi@yoshi:~$ sudo apt install i2c-tools
接続です。I2CバスにStemma QT/Qwiicコネクタで増設します。使っているセンサ・ボードはAdafruit製です。
0x76ではなく、0x77に見つけてきました。
yoshi@yoshi:~$ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
sudo nano /boot/config.txtを立ち上げ、最後の行に、次の1行を追加します。
dtoverlay = i2c-sensor,bme280,addr=0x77
リブート(restart)します。UUという表記に代わっているので、デバイス・ドライバが組み込まれたことが確認できました。
yoshi@yoshi:~$ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- UU
デバイス・ドライバが組み込まれたので、テキスト・ファイルで測定結果が常に更新されているので読み出します。
yoshi@yoshi:~$ ls /sys/bus/i2c/devices/
1-0077 i2c-1 i2c-11 i2c-12
yoshi@yoshi:~$ ls /sys/bus/i2c/devices/1-0077/iio:device0/
in_humidityrelative_input in_pressure_oversampling_ratio name subsystem
in_humidityrelative_oversampling_ratio in_temp_input of_node uevent
in_pressure_input in_temp_oversampling_ratio power waiting_for_supplier
yoshi@yoshi:~$ cat /sys/bus/i2c/devices/i2c-1/1-0077/iio:device0/in_temp_input
26330
yoshi@yoshi:~$ cat /sys/bus/i2c/devices/i2c-1/1-0077/iio:device0/in_pressure_input
101.508132812
yoshi@yoshi:~$ cat /sys/bus/i2c/devices/i2c-1/1-0077/iio:device0/in_humidityrelative_input
60806
温度は千倍になっているので、26.33℃、気圧はPaなので、1015.08132812hPa、湿度は千倍になっているので、60.806%です。
ラズパイのOS(bookworm)の場合とまったく同じです。
温度を取得するプログラム
catで読みだしていたテキスト・ファイルを、プログラムで読み出します。
yoshi@yoshi:~/ros2_ws$ nano works/src/bme280/readTemp.cpp
#include <iostream>
#include <fstream> // ファイル入出力用
#include <cstdlib> // exit()用
using namespace std;
int main(void)
{
ifstream ifs; // ifstream の変数(インスタンス)を生成.
ifs.open("/sys/bus/i2c/devices/i2c-1/1-0077/iio:device0/in_temp_input");
// ファイルを開く.fopen() に相当
if( ! ifs ) { // ファイルが開けない場合の処理
cerr << "File open error ! " << endl;
exit(1);
}
char data[32]; // データを読み込むバッファ
ifs >> data; // ifs から入力
float temp = stof(data);
cout << temp/1000.0 << endl; // 確認のため画面に出力
ifs.close(); // ファイルを閉じる.fclose()に相当.
return 0;
}
コンパイルし、実行します。
yoshi@yoshi:~/ros2_ws$ g++ -o out works/src/bme280/bme288.cpp
yoshi@yoshi:~/ros2_ws$ ./out
28.35
関数にしました。
#include <iostream>
#include <fstream> // ファイル入出力用
#include <cstdlib> // exit()用
using namespace std;
float readTemp(){
ifstream ifs; // ifstream の変数(インスタンス)を生成.
ifs.open("/sys/bus/i2c/devices/i2c-1/1-0077/iio:device0/in_temp_input");
// ファイルを開く.fopen() に相当
if( ! ifs ) { // ファイルが開けない場合の処理
cerr << "File open error ! " << endl;
exit(1);
}
char data[32]; // データを読み込むバッファ
ifs >> data; // ifs から入力
float temp = stof(data);
ifs.close(); // ファイルを閉じる.fclose()に相当.
return (float)(temp/1000.0);
}
int main(void)
{
cout << readTemp();
}
実行すると、温度を表示します。
温度をpublishする。msgは文字列
温度を読み出す事例を見つけたのですが、今の私の力では、理解できませんでした。
前回までのディレクトリ構造です。srcのなかに、bme280ディレクトリを作り、bme280.cppで、プログラムを保存します。プログラムは、前回のpub.cppに、readTemp()関数を組み込んだ形にしました。
$ cd ros2_ws
tree
└── works
├── CMakeLists.txt
├── include
│ └── works
├── package.xml
└── src
├── exec_sample
│ └── main.c
├── pub
│ └── pub.cpp
└── sub
└── sub.cpp
yoshi@yoshi:~/ros2_ws$ mkdir works/src/bme280
yoshi@yoshi:~/ros2_ws$ nano works/src/bme280/bme280.cpp
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include <stdio.h>
#include <iostream>
#include <fstream> // ファイル入出力用
#include <cstdlib> // exit()用
using namespace std;
float readTemp(){
ifstream ifs; // ifstream の変数(インスタンス)を生成.
ifs.open("/sys/bus/i2c/devices/i2c-1/1-0077/iio:device0/in_temp_input");
// ファイルを開く.fopen() に[>
if( ! ifs ) { // ファイルが開けない場合の処理
cerr << "File open error ! " << endl;
exit(1);
}
char data[32]; // データを読み込むバッファ
ifs >> data; // ifs から入力
float temp = stof(data);
ifs.close(); // ファイルを閉じる.fclose()に相当.
return (float)(temp/1000.0);
}
int main(int argc, char **argv)
{
printf("start publish bme280\n");
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("publish_bme280_temp");
auto publisher = node->create_publisher<std_msgs::msg::String>("greeting", 1);
rclcpp::WallRate loop(1);
while (rclcpp::ok()) {
auto msg = std_msgs::msg::String();
msg.data = "Hello, world " + std::to_string(readTemp());
publisher->publish(msg);
loop.sleep();
}
rclcpp::shutdown();
return 0;
}
CMakeLists.txtを修正します。
yoshi@yoshi:~/ros2_ws$ nano works/CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(works)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(std_msgs REQUIRED)
add_executable(sub src/sub/sub.cpp)
ament_target_dependencies(sub rclcpp std_msgs)
install(
TARGETS sub
DESTINATION lib/${PROJECT_NAME}
)
add_executable(bme280 src/bme280/bme280.cpp)
ament_target_dependencies(bme280 rclcpp std_msgs)
install(
TARGETS bme280
DESTINATION lib/${PROJECT_NAME}
)
ament_package()
colcon build
でビルドしたものを登録します。まえに作ったsubscriberのsubを起動します。
yoshi@yoshi:~/ros2_ws$ source ~/ros2_ws/install/setup.bash
yoshi@yoshi:~/ros2_ws$ ros2 run works sub
もう一つターミナルを立ち上げます。今回作ったpublisherのbme280を起動します。
ssh yoshi.local
yoshi@yoshi:~/ros2_ws$ source ~/ros2_ws/install/setup.bash
yoshi@yoshi:~/ros2_ws$ ros2 run works bme280
start publish bme280
subが動いているターミナルの最初の付近の様子です。実行中にBME280を指でつまんでいます。
yoshi@yoshi:~/ros2_ws$ source ~/ros2_ws/install/setup.bash
yoshi@yoshi:~/ros2_ws$ ros2 run works sub
start subscribe test
[INFO] [1722152136.834887788] [yoshi_subscriber]: Hello, world 28.420000
[INFO] [1722152137.835059203] [yoshi_subscriber]: Hello, world 28.420000
[INFO] [1722152138.835054433] [yoshi_subscriber]: Hello, world 28.420000
[INFO] [1722152139.835027311] [yoshi_subscriber]: Hello, world 28.420000
[INFO] [1722152140.835045820] [yoshi_subscriber]: Hello, world 28.420000
[INFO] [1722152141.835054939] [yoshi_subscriber]: Hello, world 29.370001
[INFO] [1722152142.835063540] [yoshi_subscriber]: Hello, world 30.180000
[INFO] [1722152143.835096604] [yoshi_subscriber]: Hello, world 30.670000
[INFO] [1722152144.835049742] [yoshi_subscriber]: Hello, world 31.000000
[INFO] [1722152145.835071232] [yoshi_subscriber]: Hello, world 31.260000
[INFO] [1722152146.835029426] [yoshi_subscriber]: Hello, world 31.450001
[INFO] [1722152147.835083157] [yoshi_subscriber]: Hello, world 31.580000
[INFO] [1722152148.835062833] [yoshi_subscriber]: Hello, world 31.290001
[INFO] [1722152149.835103249] [yoshi_subscriber]: Hello, world 31.100000
[INFO] [1722152150.835075518] [yoshi_subscriber]: Hello, world 30.980000
ディレクトリの様子です。
└── works
├── CMakeLists.txt
├── include
│ └── works
├── package.xml
└── src
├── bme280
│ ├── bme280.cpp
│ └── readTemp.cpp
├── exec_sample
│ └── main.c
├── pub
│ └── pub.cpp
└── sub
└── sub.cpp
可視化
ラズパイのターミナルで可視化ツールを動かします。
$ source /opt/ros/jazzy/setup.bash
$ rqt_graph
rqt_graphの左上にあるリフレッシュ・アイコンをクリックしたのが次の画面です。
備忘録 ラズパイ5 ROS2
① ハードの用意とUbuntu Desktop 24.04LTS
② Ubuntu Desktop 24.04LTSでROS2環境 rqt_graphとturtlesim
③ Ubuntu Desktop 24.04LTSでROS2環境 Python その1 responder(セットアップ)
④ Ubuntu Desktop 24.04LTSでROS2環境 Pythonその2responder(コーディングと実行;失敗)
⑤ Ubuntu Desktop 24.04LTSでROS2環境 C++ その1 セットアップ main.c
⑥ Ubuntu Desktop 24.04LTSでROS2環境 C++ その2 セットアップ pub.cpp rqt_graph
⑦ Ubuntu Desktop 24.04LTSでROS2環境 C++ その3 セットアップ sub.cpp rqt_graph
⑧ Ubuntu Desktop 24.04LTSでROS2環境 C++ その4 bme280.cpp 温度
⑨ Ubuntu Desktop 24.04LTSでROS2環境 C++ その5 bme280_3.cpp 温度、湿度、気圧
⑩ Ubuntu Desktop 24.04LTSでROS2環境 C++ その6 VL53L1X.cpp 距離センサ