我的目标是根据 BNO055 惯性测量单元的欧拉角来控制 Nema 17 步进电机的位置和速度。我正在使用 ESP32 通过 WIFI 将代码刷新到 rosserial。我使用 12V 电源为 Nema 17 供电,使用小型外部 5V 电池组为 BNO055 供电。
总之,步进电机应在 0-4100 步之间移动,这将映射到 BNO055 y 轴的 -90 度和 90 度。
为此,我需要尽可能多地读取 BNO055 传感器的输出,并且仅在 BNO055 相对于映射改变位置时才改变 Nema 17 的方向。
我遇到的问题是,当我在代码中读取传感器时,我的电机开始抖动并且旋转不顺畅。我想知道如何让这两件事同时工作(读取传感器和移动 nema 17)。
PS:我将通过使用 BNO055 传感器计算 PI 控制并相应地调整 delayMicroseconds() 来控制速度……但首先要使读数和电机运动平稳。
下面是我用来调试这个问题的代码片段:
#include <WiFi.h>
#include <ros.h>
#include <Wire.h>
#include <std_msgs/Header.h>
#include <std_msgs/String.h>
#include <geometry_msgs/Quaternion.h>
#include <HardwareSerial.h>
#include <analogWrite.h>
#include <MultiStepper.h>
#include <AccelStepper.h>
#include <Stepper.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>
#include <math.h>
//////////////////////
// BNO055 //
//////////////////////
Adafruit_BNO055 bno_master = Adafruit_BNO055(55, 0x29);
Adafruit_BNO055 bno_slave = Adafruit_BNO055(55, 0x28);
geometry_msgs::Quaternion Quaternion;
std_msgs::String imu_msg;
#define I2C_SDA 21
#define I2C_SCL 22
TwoWire I2Cbno = TwoWire(0); // I2C connection will increase 6Hz data transmission
float ax_m, ay_m, az_m, ax_s, ay_s, az_s; // accelerometer
float gw_m, gx_m, gy_m, gz_m, gw_s, gx_s, gy_s, gz_s; // gyroscope
float ex_m, ey_m, ez_m, ex_s, ey_s, ez_s; // euler
float qw_m, qx_m, qy_m, qz_m, qw_s, qx_s, qy_s, qz_s; // quaternions
//////////////////////
// WiFi Definitions //
//////////////////////
const char* ssid = "FRITZ!Box 7430 PN"; // Sebas: "WLAN-481774"; Paula: "FRITZ!Box 7430 PN"; ICS: ICS24; Hotel Citadelle Blaye
const char* password = "37851923282869978396"; // Sebas: "Kerriganrocks!1337"; Paula: "37851923282869978396"; ICS: uZ)7xQ*0; citadelle
IPAddress server(192,168,178,112); // ip of your ROS server
IPAddress ip_address;
WiFiClient client;
int status = WL_IDLE_STATUS;
//long motorTimer = 0, getImuDataTimer = 0, millisNew = 0; //millisOld = 0,
//////////////////////
// Stepper motor //
//////////////////////
int stepPin = 4;
int stepPinState = LOW;
int dirPin = 2;
int dirPinState = HIGH;
unsigned long millisOld1 = 0;
unsigned long millisOld2 = 0;
long motorTimer = 1; // in milliseconds
long getImuDataTimer = 10; // in milliseconds
double maxPosition = 4100;
double stepsMoved = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class WiFiHardware {
public:
WiFiHardware() {};
void init() {
// do your initialization here. this probably includes TCP server/client setup
client.connect(server, 11411);
}
// read a byte from the serial port. -1 = failure
int read() {
// implement this method so that it reads a byte from the TCP connection and returns it
// you may return -1 is there is an error; for example if the TCP connection is not open
return client.read(); //will return -1 when it will works
}
// write data to the connection to ROS
void write(uint8_t* data, int length) {
// implement this so that it takes the arguments and writes or prints them to the TCP connection
for(int i=0; i<length; i++)
client.write(data[i]);
}
// returns milliseconds since start of program
unsigned long time() {
return millis(); // easy; did this one for you
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int i;
void chatterCallback(const std_msgs::String& msg) {
i = atoi(msg.data);
// s.write(i);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setupWiFi()
{
// WIFI setup
WiFi.begin(ssid, password);
Serial.print("\nConnecting to "); Serial.println(ssid);
uint8_t i = 0;
while (WiFi.status() != WL_CONNECTED && i++ < 20) delay(500);
if(i == 21){
Serial.print("Could not connect to"); Serial.println(ssid);
while(1) delay(500);
}
Serial.print("Ready! Use ");
Serial.print(WiFi.localIP());
Serial.println(" to access client");
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ros::Subscriber<std_msgs::String> sub("message", &chatterCallback);
ros::Publisher pub("imu_data/", &imu_msg);
ros::NodeHandle_<WiFiHardware> nh;
void setup() {
// set the digital pins as outputs
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
Serial.begin(57600);
setupWiFi();
// I2C connection IMUs
Wire.begin(I2C_SDA, I2C_SCL);
I2Cbno.begin(I2C_SDA, I2C_SCL, 400000);
bno_master.begin();
bno_slave.begin();
// get imu calibrations
uint8_t system, gyro, accel, mg = 0;
bno_master.getCalibration(&system, &gyro, &accel, &mg);
bno_slave.getCalibration(&system, &gyro, &accel, &mg);
bno_master.setExtCrystalUse(true);
bno_slave.setExtCrystalUse(true);
nh.initNode();
nh.advertise(pub);
}
/////////////////////////////
/// GET IMU DATA FUNCTION ///
/////////////////////////////
int get_imu_data(){
imu::Vector<3> Euler_s = bno_slave.getVector(Adafruit_BNO055::VECTOR_EULER); // 100 Hz capacity by BNO055 // IF I COMMENT THIS LINE OUT AND SET VARIABLES BELOW TO SET VALUES, MY MOTOR RUNS PERFECTLY
// Euler
float ex_s = Euler_s.x();
float ey_s = Euler_s.y();
float ez_s = Euler_s.z();
// putting data into string since adding accel, gyro, and both imu data becomes too cumbersome for rosserial buffer size. String is better for speed of data
String data = String(ex_s) + "," + String(ey_s) + "," + String(ez_s) + "!";
int length_data = data.indexOf("!") + 1;
char data_final[length_data + 1];
data.toCharArray(data_final, length_data + 1);
imu_msg.data = data_final;
pub.publish(&imu_msg);
nh.spinOnce();
Serial.println(ey_s);
return ey_s; // ex_s, ey_s, ez_s
}
/////////////////////////////
// MAIN LOOP //
/////////////////////////////
void loop() {
unsigned long currentMillis = millis();
//////////////////
// GET IMU DATA //
//////////////////
if(currentMillis - millisOld2 >= getImuDataTimer)
{
ey_s = get_imu_data();
Serial.print(ey_s);
}
////////////////
// MOVE MOTOR //
////////////////
// later, the direction will depend on the output of ey_s
if((dirPinState == HIGH) && (currentMillis - millisOld1 >= motorTimer))
{
if(stepsMoved <= maxPosition)
{
digitalWrite(dirPin, dirPinState);
millisOld1 = currentMillis; // update time
stepsMoved += 5;
for(int i =0; i<=5; i++)
{
digitalWrite(stepPin, HIGH);
delayMicroseconds(1200); // constant speed
digitalWrite(stepPin, LOW);
}
Serial.println(stepsMoved); // checking
}
else if(stepsMoved > maxPosition)
{
dirPinState = LOW;
millisOld1 = currentMillis; // update time
stepsMoved = 0;
}
}
if((dirPinState == LOW) && (currentMillis - millisOld1 >= motorTimer))
{
if(stepsMoved <= maxPosition)
{
digitalWrite(dirPin, dirPinState);
millisOld1 = currentMillis; // update time
stepsMoved += 5;
for(int i =0; i<=5; i++)
{
digitalWrite(stepPin, HIGH);
delayMicroseconds(1200); // constant speed
digitalWrite(stepPin, LOW);
}
Serial.println(stepsMoved); // checking
}
else if(stepsMoved > maxPosition)
{
dirPinState = HIGH;
millisOld1 = currentMillis; // update time
stepsMoved = 0;
}
}
}
我已经尝试过 AccelStepper.h 库,但在位置控制和速度更新方面没有获得所需的输出。