# Rpi(或電腦)與Arduino溝通(with ROS)
## 1. Arduino ros的設置
首先,安裝Arduino IDE(https://www.arduino.cc/en/software),作為燒入程式碼的工具。
接著,手動安裝函式庫(**Rosserial_Arduino_Library**):
### Example arduino code:
```=arduino
#include <ros.h>
#include <std_msgs/Int64.h>
#include <Servo.h>
Servo servo;
ros::NodeHandle nh;
void messageCb( const std_msgs::Int64& msg){
servo.write(msg.data);//write the angle
}
ros::Subscriber<std_msgs::Int64> sub("Servo_topic", &messageCb );
void setup()
{
Serial.begin(9600);
pinMode(9, OUTPUT);
servo.attach(9);
servo.write(1);
nh.initNode();
nh.subscribe(sub);
}
void loop()
{
nh.spinOnce();
delay(1);
}
```
## 2. Docker與ros設置
### (1) Wsl 連線 USB 裝置
首先,讓windows上的裝置(device)能分享到wsl上:
```
# windows下
$ winget install --interactive --exact dorssel.usbipd-win
# wsl linux下
#如果出現" WSL 'usbip' client not correctly installed. ",就再重裝一次
$ sudo hwclock --hctosys
$ sudo apt-get -o Acquire::Check-Valid-Until=false -o Acquire::Check-Date=false update
$ sudo apt install linux-tools-virtual hwdata
$ sudo update-alternatives --install /usr/local/bin/usbip usbip `ls /usr/lib/linux-tools/*/usbip | tail -n1` 20
```
記得重新開機!!
使用指令參考:
```
#檢查各個設備的使用狀態
$ usbipd wsl list
#分享ID為"1-3"的設備
$ usbipd wsl attach --busid 1-3
#停止分享ID為"1-3"的設備
$ usbipd wsl detach --busid 1-3
```
範例圖:

如此可在wsl或ubuntu中使用(下圖為ubuntu環境下的示意):

---
### (2) 引入docker image(映像檔)、宣告container
[Docker] 建立群組
https://quietbo.com/2022/06/06/docker-建立群組-got-permission-denied-while-trying-to-connect-to-the-docker-daemon-socket-at-unix解決/
```=linux
# 列出images
$ docker images -a
# pull引入image
# docker pull {Docker hub user}/{image_name}:{tag}
$ docker pull dongdonghsu065/ros_test:latest
$ docker images -a
# 以此image來宣告container
$ docker run -itd --name {container name} --hostname {host name} --device=/dev/ttyACM0:/dev/ttyACM0 {image_name}:{tag}
# 列出containers
$ docker ps -a
#進入container
$ docker exec -it {container_name} bash
```
Example talker code:
```=C++
#include "ros/ros.h"
#include "std_msgs/Int64.h"
#include <sstream>
/**
* This tutorial demonstrates simple sending of messages over the ROS system.
*/
int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line.
* For programmatic remappings you can use a different version of init() which takes
* remappings directly, but for most command-line programs, passing argc and argv is
* the easiest way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "talker");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* The advertise() function is how you tell ROS that you want to
* publish on a given topic name. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. After this advertise() call is made, the master
* node will notify anyone who is trying to subscribe to this topic name,
* and they will in turn negotiate a peer-to-peer connection with this
* node. advertise() returns a Publisher object which allows you to
* publish messages on that topic through a call to publish(). Once
* all copies of the returned Publisher object are destroyed, the topic
* will be automatically unadvertised.
*
* The second parameter to advertise() is the size of the message queue
* used for publishing messages. If messages are published more quickly
* than we can send them, the number here specifies how many messages to
* buffer up before throwing some away.
*/
ros::Publisher chatter_pub = n.advertise<std_msgs::Int64>("Servo_topic", 1000);
ros::Rate loop_rate(1); //1Hz
/**
* A count of how many messages we have sent. This is used to create
* a unique string for each message.
*/
int count = 1;
while (ros::ok())
{
/**
* This is a message object. You stuff it with data, and then publish it.
*/
std_msgs::Int64 msg;
msg.data = count;
//ROS_INFO("%s", msg.data;
/**
* The publish() function is how you send messages. The parameter
* is the message object. The type of this object must agree with the type
* given as a template parameter to the advertise<>() call, as was done
* in the constructor above.
*/
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
if(count == 180)
count = 1;
else if(count == 1){
count = 45;
}
else
count += 45;
}
return 0;
}
```
### (3) 完成arduino與電腦間的溝通(with ros)
首先,讓ros環境可以運行,使用指令(會用到三個視窗):
```
$ roscore
```
接著,開啟我們arduino裡的ros_node(這裡是subscriber):
```
$ rosrun rosserial_python serial_node.py _port:=/dev/ttyACM0
```
最後,開啟我們電腦內的ros_node(這裡是publisher):
```
# 進入workspace、source資源
$ cd ~/catkin_ws
$ source devel/setup.bash
# 編譯
$ catkin_make
#跑talker_node
$ rosrun servo_test talker
# 檢查topic是否存在
$ rostopic list
# 檢查特定topic的資料
$ rostopic echo {topic_name}
```
根據希望的角度變化,可以更改"~/catkin_ws/src/servo_test/src/servo_angle.cpp"的code。
---
教學錄影: https://www.youtube.com/watch?v=D6z961M7gVQ&ab_channel=DongdongHsu
其它參考網址:
1. 【WSL 2】配置连接 USB 设备并使用主机的 USB 摄像头
https://github.com/dorssel/usbipd-win/wiki/WSL-support
https://learn.microsoft.com/zh-tw/windows/wsl/connect-usb
2. 在樹梅派上跑ROS控制Arduino Servo
https://harry123180.medium.com/%E5%9C%A8%E6%A8%B9%E6%A2%85%E6%B4%BE%E4%B8%8A%E8%B7%91ros%E6%8E%A7%E5%88%B6arduino-servo-6823f69276f0
3. ROS Tutorials
http://wiki.ros.org/ROS/Tutorials?fbclid=IwAR0TnA-7-gvzoATBM8wF55PySEjDwu6ZqAzhgVC3bzbujXCY03VXN3ksRiE
4. 2022AUV軟體培訓-Servo馬達
https://hackmd.io/P950P583QpKtpE-pQGrSyg
5. 認識UART、I2C、SPI三介面特性
https://makerpro.cc/2016/07/learning-interfaces-about-uart-i2c-spi/
6. Docker Desktop Tutorial
https://hackmd.io/oeLOi6P-SjyV5HPMYqZdxw
---