Self Driving and ROS 2 - Learn by Doing! Odometry & Control: Arduino (セクション12-2/13)
セクション12の2/5では、ArduinoとROS 2の通信を確立し、DCモーターとエンコーダの制御方法を学びました。
講義145から150では、Arduinoのシリアル通信、PythonおよびC++でのサブスクライバーノードの作成、DCモーター制御の実装が行われました。
最後に、エンコーダを使用してホイールの回転速度を測定し、ROS 2トピックにデータを公開する方法を学びました。
「Self Driving and ROS 2 - Learn by Doing! Odometry & Control」コースのセクション12では、完全に機能するロボットの作成にさらに深く取り組みます。このブログ投稿では、ArduinoとROS 2の間の堅牢な通信を確立し、DCモーターとエンコーダの制御を実装することに焦点を当てた、第145から150講義を探索します。
講義145:Arduinoとのサブスクライバーノード
この講義では、シリアルポート経由でメッセージを送信するArduinoスクリプトを開発し、これらのメッセージを受信するサブスクライバーノードを作成します。
Arduinoスクリプト:Simple Serial Transmitter
int x = 0;
void setup() {
Serial.begin(115200);
Serial.setTimeout(1);
}
void loop() {
Serial.println(x);
x++;
delay(100);
}
このシンプルなスクリプトは、115200のボーレートでシリアル通信を初期化し、シリアルポート経由でインクリメントされる整数を繰り返し送信します。
コンパイルとアップロード
スクリプトを書いた後は、Arduino IDEを使用してコンパイルし、Arduinoボードにアップロードします。
講義146:Pythonサブスクライバーノード
この講義では、Arduinoからのメッセージを受信し、それらをROS 2トピックに再公開するPython ROS 2ノードの作成に焦点を当てます。
Pythonスクリプト:Simple Serial Receiver
#!/usr/bin/env python3
import serial
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class SimpleSerialReceiver(Node):
def __init__(self):
super().__init__("simple_serial_receiver")
self.declare_parameter("port", "/dev/ttyUSB0")
self.declare_parameter("baudrate", 115200)
self.port_ = self.get_parameter("port").value
self.baudrate_ = self.get_parameter("baudrate").value
self.pub_ = self.create_publisher(String, "serial_receiver", 10)
self.arduino_ = serial.Serial(port=self.port_, baudrate=self.baudrate_, timeout=0.1)
self.frequency_ = 0.01
self.timer_ = self.create_timer(self.frequency_, self.timerCallback)
def timerCallback(self):
if rclpy.ok() and self.arduino_.is_open:
data = self.arduino_.readline()
try:
data.decode("utf-8")
except:
return
msg = String()
msg.data = str(data)
self.pub_.publish(msg)
def main():
rclpy.init()
simple_serial_receiver = SimpleSerialReceiver()
rclpy.spin(simple_serial_receiver)
simple_serial_receiver.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
このスクリプトは、シリアル接続を初期化し、Arduinoから読み取ったデータを「serial_receiver」トピックに公開するROS 2ノードをセットアップします。
CMakeLists.txtの更新
新しいPythonスクリプトをROS 2パッケージに統合するために、`CMakeLists.txt`ファイルを更新し、Pythonの依存関係とインストール手順を追加します。
講義147:C++サブスクライバーノード
ここでは、同様の機能をC++を使用して開発します。
C++スクリプト:Simple Serial Receiver
#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>
#include <libserial/SerialPort.h>
class SimpleSerialReceiver : public rclcpp::Node {
public:
SimpleSerialReceiver() : Node("simple_serial_receiver") {
declare_parameter<std::string>("port", "/dev/ttyUSB0");
port_ = get_parameter("port").as_string();
arduino_.Open(port_);
arduino_.SetBaudRate(LibSerial::BaudRate::BAUD_115200);
pub_ = create_publisher<std_msgs::msg::String>("serial_receiver", 10);
timer_ = create_wall_timer(0.01s, std::bind(&SimpleSerialReceiver::timerCallback, this));
}
~SimpleSerialReceiver() {
arduino_.Close();
}
void timerCallback() {
if(rclcpp::ok() && arduino_.IsDataAvailable()) {
auto message = std_msgs::msg::String();
arduino_.ReadLine(message.data);
pub_->publish(message);
}
}
private:
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_;
rclcpp::TimerBase::SharedPtr timer_;
std::string port_;
LibSerial::SerialPort arduino_;
};
int main(int argc, char* argv[]) {
rclcpp::init(argc, argv);
auto node = std::make_shared<SimpleSerialReceiver>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
このC++スクリプトは、Arduinoのシリアルポートから読み取ったデータを「serial_receiver」トピックに公開するROS 2ノードをセットアップします。
講義148:サブスクライバーノードのテスト
すべてが期待通りに機能することを確認するために、テストは重要です。
テスト手順:
サブスクライバーノードの起動:PythonまたはC++のサブスクライバーノードを実行します。
トピックのリスト表示:`ros2 topic list`を使用して「serial_receiver」トピックが利用可能であることを確認します。
トピックのエコー:`ros2 topic echo /serial_receiver`を使用してArduinoから受信したメッセージを表示します。
講義149:Arduinoを使用したDCモーター制御
この講義では、ArduinoとROS 2を使用してDCモーターを制御する方法を説明します。
Arduinoスクリプト:Simple Motor Control
#define L298N_enA 9 // PWM
#define L298N_in2 13 // Dir Motor A
#define L298N_in1 12 // Dir Motor A
float cmd = 0;
void setup() {
pinMode(L298N_enA, OUTPUT);
pinMode(L298N_in1, OUTPUT);
pinMode(L298N_in2, OUTPUT);
digitalWrite(L298N_in1, HIGH);
digitalWrite(L298N_in2, LOW);
Serial.begin(115200);
}
void loop() {
if (Serial.available()) {
cmd = Serial.readString().toFloat();
}
analogWrite(L298N_enA, cmd * 100);
}
このスクリプトは、L298Nモータードライバとシリアルコマンドを使用してDCモーターの速度と方向を制御するためにArduinoを設定します。
ハードウェアの設定
スキーマティックに従ってArduino、L298Nモータードライバ、およびDCモーターを接続します。すべての接続が確実で、電源供給が適切であることを確認してください。
講義150:Arduinoを使用したエンコーダ
この講義では、モーターの回転速度を測定するためにエンコーダを使用する方法を説明します。
Arduinoスクリプト:Simple Encoder Reader
#define L298N_enA 9 // PWM
#define L298N_in2 13 // Dir Motor A
#define L298N_in1 12 // Dir Motor A
#define right_encoder_phaseA 3 // Interrupt
#define right_encoder_phaseB 5
unsigned int right_encoder_counter = 0;
String right_encoder_sign = "p";
double right_wheel_meas_vel = 0.0; // rad/s
void setup() {
pinMode(L298N_enA, OUTPUT);
pinMode(L298N_in1, OUTPUT);
pinMode(L298N_in2, OUTPUT);
digitalWrite(L298N_in1, HIGH);
digitalWrite(L298N_in2, LOW);
Serial.begin(115200);
pinMode(right_encoder_phaseB, INPUT);
attachInterrupt(digitalPinToInterrupt(right_encoder_phaseA), rightEncoderCallback, RISING);
}
void loop() {
right_wheel_meas_vel = (10 * right_encoder_counter * (60.0 / 385.0)) * 0.10472;
String encoder_read = "r" + right_encoder_sign + String(right_wheel_meas_vel);
Serial
.println(encoder_read);
right_encoder_counter = 0;
analogWrite(L298N_enA, 100);
delay(100);
}
void rightEncoderCallback() {
if(digitalRead(right_encoder_phaseB) == HIGH) {
right_encoder_sign = "p";
} else {
right_encoder_sign = "n";
}
right_encoder_counter++;
}
このスクリプトは、エンコーダからデータを読み取り、ホイールの回転速度を計算し、シリアルポート経由でデータを公開します。
結論
セクション12の第145から150講義では、応答性の高いインタラクティブなロボットを作成するための重要な構成要素が提供されます。ROS 2とArduinoの間の通信を確立し、DCモーターとエンコーダの制御を実装することで、洗練された自律走行ロボットの開発に向けた重要なステップを踏み出しています。このエキサイティングなロボティクスの旅を続けながら、さらなる洞察をお届けしますので、お楽しみに!
この記事が気に入ったらサポートをしてみませんか?