【从零开始的ROS四轴机械臂控制】(七)- ROS与arduino连接
從零開始的ROS四軸機械臂控制(七)
- 十、ROS與arduino連接
- 1.虛擬機與arduino的連接
- (1)arduino連接與IDE
- (2)PCA9685模塊支持與測試
- 2.ROS與arduino連接測試
- (1)Arduino安裝rosserial
- (2)rosserial測試
- 3.ROS與arduino連接
- (1)ros2ard節點
- (2)arm_command節點
- (3)arduino訂閱
- (4)批處理
- (5)程序運行
- 十一、小結
十、ROS與arduino連接
回到許久沒有動的硬件上來,考慮到我使用的是虛擬機,所以首先要測試的是虛擬機與arduino的連接,然后才能考慮ROS與arduino的連接。
寫這部分的時候不知道為什么markdown編輯器“炸”掉了,編輯好的文本成了一堆亂碼,關鍵這堆亂碼還被保存了。由于之前沒有出現過這種情況,我習慣性的用編輯器做記錄,現在基本上都沒了。于是就導致了一個問題,我可能在重新編輯的時候忘記某些步驟,還是希望能注意一下。
本部分完成效果如下
1.虛擬機與arduino的連接
(1)arduino連接與IDE
首先將Arduino的連接到虛擬機,只要在Vmware打開的情況下,它都會詢問是否連接到虛擬機,或者直接在Player菜單–>可移動設備中連接也可。
然后在虛擬機中安裝arduino IDE,在Linux安裝的具體方法可以百度或者Google上,在此不做詳細說明。安裝之后可以打開如下。
(2)PCA9685模塊支持與測試
以上為原理圖,連接好線后我們要下載關于PCA9685模塊的支持庫進行測試。
在arudino IDE中打開庫管理,搜索Adafruit pwm Servo Driver Library,然后進行安裝
在該庫的示例下打開servo示例如下,
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);// Depending on your servo make, the pulse width min and max may vary, you
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!
#define SERVOMIN 150 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 600 // This is the 'maximum' pulse length count (out of 4096)
#define USMIN 600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX 2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates// our servo # counter
uint8_t servonum = 0;void setup() {Serial.begin(9600);Serial.println("8 channel Servo test!");pwm.begin();// In theory the internal oscillator is 25MHz but it really isn't// that precise. You can 'calibrate' by tweaking this number till// you get the frequency you're expecting!pwm.setOscillatorFrequency(27000000); // The int.osc. is closer to 27MHz pwm.setPWMFreq(SERVO_FREQ); // Analog servos run at ~50 Hz updatesdelay(10);
}// You can use this function if you'd like to set the pulse length in seconds
// e.g. setServoPulse(0, 0.001) is a ~1 millisecond pulse width. It's not precise!
void setServoPulse(uint8_t n, double pulse) {double pulselength;pulselength = 1000000; // 1,000,000 us per secondpulselength /= SERVO_FREQ; // Analog servos run at ~60 Hz updatesSerial.print(pulselength); Serial.println(" us per period"); pulselength /= 4096; // 12 bits of resolutionSerial.print(pulselength); Serial.println(" us per bit"); pulse *= 1000000; // convert input seconds to uspulse /= pulselength;Serial.println(pulse);pwm.setPWM(n, 0, pulse);
}void loop() {// Drive each servo one at a time using setPWM()Serial.println(servonum);for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {pwm.setPWM(servonum, 0, pulselen);}delay(500);for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {pwm.setPWM(servonum, 0, pulselen);}delay(500);// Drive each servo one at a time using writeMicroseconds(), it's not precise due to calculation rounding!// The writeMicroseconds() function is used to mimic the Arduino Servo library writeMicroseconds() behavior. for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) {pwm.writeMicroseconds(servonum, microsec);}delay(500);for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) {pwm.writeMicroseconds(servonum, microsec);}delay(500);servonum++;if (servonum > 7) servonum = 0; // Testing the first 8 servo channels
}
一個比較好的建議是,把以下部分的范圍改小,否則可能會損壞模型
#define SERVOMIN 150 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 600 // This is the 'maximum' pulse length count (out of 4096)
#define USMIN 600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX 2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
使用IDE連接好arduino UNO后,上載程序,極大可能報錯
[Errno 13] Permission denied: '/dev/ttyACM0'
說明沒有權限,更改權限就可以了
$ sudo chmod 777 /dev/ttyACM0
程序順利上傳,不過這種方法只是臨時處理,在下次啟動時需要重新輸入。
若正確接線,效果如下,
2.ROS與arduino連接測試
(1)Arduino安裝rosserial
運行命令安裝rosserial_arduino包,
$ sudo apt-get install ros-indigo-rosserial-arduino
$ sudo apt-get install ros-indigo-rosserial
在庫管理中添加Rosserial Arduino庫
可以打開示例如下,
(2)rosserial測試
使用blink程序進行測試,打開示例程序,
#include <ros.h>
#include <std_msgs/Empty.h>ros::NodeHandle nh;void messageCb( const std_msgs::Empty& toggle_msg){digitalWrite(13, HIGH-digitalRead(13)); // blink the led
}ros::Subscriber<std_msgs::Empty> sub("toggle_led", &messageCb );void setup()
{pinMode(13, OUTPUT);nh.initNode();nh.subscribe(sub);
}void loop()
{nh.spinOnce();delay(1);
}
將程序上載到arduino后,打開一個終端運行命令,
$ roscore
接下來,運行rosserial客戶端應用程序,它將Arduino消息轉發給其他ROS。確保使用正確的串口:
rosrun rosserial_python serial_node.py /dev/ttyACM0
用rostopic切換LED:
rostopic pub toggle_led std_msgs/Empty --once
若運行正常,則每輸入一次命令,燈會進行一次操作。
3.ROS與arduino連接
在完成了所有準備工作之后我們就可以進行下一步了
(1)ros2ard節點
因為舵機的控制信息和我們計算使用的角度信息不相同,所以我們就需要一個轉換節點,接收節點信息,并進行轉換,發布給arduino
#!/usr/bin/env pythonimport math
import rospy
from std_msgs.msg import UInt16MultiArray, Float64MultiArray
from sensor_msgs.msg import JointState
from arm1.srv import *class ros2ard(object):def __init__(self):node_name = 'ros2ard'rospy.init_node(node_name)# init Publisherself.pub1 = rospy.Publisher('/ros2ard/joint_msg', UInt16MultiArray, queue_size=10)# init Subscriberrospy.Subscriber('/arm_command/joint_val', Float64MultiArray, self.callback)# 注意這里的各種參數是經過測試計算之后得出的,不具有普適性def cal(self, angle):joint_value = int(1.89*91.80/1.57*angle) return joint_valuedef callback(self, joint_msg):print(joint_msg.data)# 注意這里的各種參數是經過測試之后得出的,+600是因為我們使用的是UInt,為了確保角度不為負數joint0_msg = self.cal(joint_msg.data[0])joint1_msg = 390 - self.cal(joint_msg.data[1])+ 600joint2_msg = 210 - self.cal(joint_msg.data[2])+ 600joint3_msg = 230 - self.cal(joint_msg.data[3])+ 600joint4_msg = 300+ 600joint_value = [joint0_msg, joint1_msg, joint2_msg, joint3_msg, joint4_msg]joint_message = UInt16MultiArray(data=joint_value)print('joint_msg', joint_message.data)self.pub1.publish(joint_message)if __name__ == '__main__':try:ros2ard()rospy.spin()except rospy.ROSInterruptException:pass
(2)arm_command節點
為了給ros2ard節點發布角度信息,所以我們需要修改一下arm_command,使其發布節點信息
#!/usr/bin/env pythonimport math
import rospy
from std_msgs.msg import Float64, Bool, Float64MultiArray
from sensor_msgs.msg import JointState
from arm1.srv import *class ArmCommand(object):def __init__(self):...self.pub3 = rospy.Publisher('/arm_command/joint_val', Float64MultiArray, queue_size=10)...# set all positions of joints with this functiondef arm_pos_set(self, joint1=0.0, joint2=0.52,joint3=-1.57,joint4=0.98,right_joint=1.0,left_joint=1.0,pos_init=False):self.msg.joint1 = joint1self.msg.joint2 = self.value_translation(joint2, 'joint2', 'cal2gaz')self.msg.joint3 = self.value_translation(joint3, 'joint3', 'cal2gaz')self.msg.joint4 = self.value_translation(joint4, 'joint4', 'cal2gaz')self.msg.right_joint = right_jointself.msg.left_joint = left_jointresponse = self.arm_mover(self.msg)joint_value = [joint1, joint2, joint3, joint4, right_joint]joint_msg = Float64MultiArray(data=joint_value)self.pub3.publish(joint_msg)if pos_init:rospy.loginfo('init all joints position')...
...
(3)arduino訂閱
我們需要使arduino訂閱節點信息,并控制舵機移動,程序編寫如下,
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include <WProgram.h>
#endif#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <ros.h>
#include <std_msgs/UInt16MultiArray.h>Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();#define SERVO_FREQ 50 ros::NodeHandle nh;void servo_set(const std_msgs::UInt16MultiArray& cmd_msg) { pwm.setPWM(0, 0, cmd_msg.data[0]-600);pwm.setPWM(1, 0, cmd_msg.data[1]-600);pwm.setPWM(2, 0, cmd_msg.data[2]-600);pwm.setPWM(3, 0, cmd_msg.data[3]-600);pwm.setPWM(4, 0, cmd_msg.data[4]-600);digitalWrite(13, HIGH - digitalRead(13));
}ros::Subscriber<std_msgs::UInt16MultiArray> sub("/ros2ard/joint_msg", servo_set);void setup() {pinMode(13, OUTPUT);pwm.begin();pwm.setOscillatorFrequency(27000000); pwm.setPWMFreq(SERVO_FREQ); pwm.setPWM(0, 0, 400);pwm.setPWM(1, 0, 270);pwm.setPWM(2, 0, 450);pwm.setPWM(3, 0, 130);pwm.setPWM(4, 0, 300);digitalWrite(13, HIGH - digitalRead(13));nh.initNode();nh.subscribe(sub);}void loop() {nh.spinOnce();delay(1000);
}
(4)批處理
由于需要啟動的東西有點多了,所以一個比較好的方法使利用批處理命令,建立setup.sh文件,
#!/bin/bashsource ~/catkin_ws/devel/setup.bash{
gnome-terminal -t "img_process" -x bash -c "rosrun arm1 image_process;exec bash"
}&sleep 5s{
gnome-terminal -t "arm_command" -x bash -c "rosrun arm1 arm_command;exec bash"
}&sleep 5s{
gnome-terminal -t "ros2ard" -x bash -c "rosrun arm1 ros2ard;exec bash"
}&sleep 5s
{
gnome-terminal -t "ros2ard" -x bash -c "rosrun rosserial_python serial_node.py /dev/ttyACM0;exec bash"
}&
這樣我們比較方便的運行命令了
(5)程序運行
運行命令打開gazebo
$ source ~/catkin_ws/devel/setup.bash
$ roslaunch arm1 gazebo.launch
新建終端,
./setup.sh
就可以像之前一樣運行程序了,打開rqt_graph 查看節點圖
效果如下,
十一、小結
不知為何,最近虛擬機非??D,以至于無法完成正常的圖像處理,不過所有預想的功能都已經實現了,所以這個arm0項目就算是完成了吧。畢竟初衷就是一個練手項目,有很多優化沒有時間去做。而且虛擬機還因為win10強制更新而導致的強制關閉出錯了,估計也需要重新安裝一遍系統。
接下來就是SLAM了,等后面學習完SLAM將其結合起來,這又是下一個項目了。
總結
以上是生活随笔為你收集整理的【从零开始的ROS四轴机械臂控制】(七)- ROS与arduino连接的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: docker构建打包java项目
- 下一篇: 从零开始的ROS四轴机械臂控制-目录