因此,我尝试使用 smbus2 库中的
write_byte
函数,并成功使用 Raspberry Pi 5 中的该函数来打开和关闭连接到 Arduino Mega 的 LED。
我的项目涉及 3 RPS平行轴机械手由 3 个步进器控制,我正在通过 Pi 5 使用计算机视觉进行数据采集,计算我希望每个机械手电机达到的必要速度、步数和加速度。对于当前的测试,我只为 3 个电机中的每一个发送一组步数、速度和加速度的样本值。由于步长/速度/加速度的值可能高于 255,我认为我需要向 Arduino 发送两个字节,而不是为通过 i2c 发送的每个值发送一个字节,并且必须使用
write_i2c_block_data function
由于我也无法真正使用串行监视器调试arduino代码,因为考虑到它通过USB电缆直接连接到Pi 5,所以我尝试在程序的不同部分打开LED,发现它没有进入“
receiveEvent
”函数如下面的代码所示。
这里可能有什么问题?我在 Pi 5 端没有收到任何错误,当我运行代码(这是 arduino 代码的初始化部分)时,电机会回家,但电机根本不会响应已发送的 i2c 数据而移动。发送给他们。
RPi 5 上的 Python 代码:
from smbus2 import SMBus
from time import sleep
import struct
addr = 0x8 # bus address
bus = SMBus(1) # indicates /dev/i2c-1
steps = [600, 600, 600]
speeds = [500, 500, 500]
accels = [200, 200, 200]
ByteSteps = [0,0,0]
ByteSpeeds = [0,0,0]
ByteAccels = [0,0,0]
for i in range(0,3):
ByteSteps[i] = list(struct.pack('>h', steps[i]))
ByteSpeeds[i] = list(struct.pack('>h', speeds[i]))
ByteAccels[i] = list(struct.pack('>h', accels[i]))
while True:
for i in range (0,3):
bus.write_i2c_block_data(addr, 0, ByteSteps[i])
sleep(0.001)
bus.write_i2c_block_data(addr, 0, ByteSpeeds[i])
sleep(0.001)
bus.write_i2c_block_data(addr, 0, ByteAccels[i])
sleep(0.001)
sleep(2)
这是 Arduino 代码:
#include <Wire.h>
#include <AccelStepper.h>
#include <cvzone.h>
// Define motors config
const int MAX_STEPPERS = 3;
const int STEP_PINS[MAX_STEPPERS] = {3, 6, 9}; // Step pins for motors 1, 2, 3
const int DIR_PINS[MAX_STEPPERS] = {2, 5, 8}; // Direction pins for motors 1, 2, 3
// Create instances of AccelStepper for each motor
AccelStepper steppers[MAX_STEPPERS] = {
AccelStepper(AccelStepper::DRIVER, STEP_PINS[0], DIR_PINS[0]),
AccelStepper(AccelStepper::DRIVER, STEP_PINS[1], DIR_PINS[1]),
AccelStepper(AccelStepper::DRIVER, STEP_PINS[2], DIR_PINS[2])
};
// Array to store received parameters for each motor
volatile long receivedSteps[MAX_STEPPERS] = {1, 0, 0}; // Initialize with 0 steps
volatile long receivedSpeed[MAX_STEPPERS] = {1, 0, 0}; // Initialize with 0 speed
volatile long receivedAcceleration[MAX_STEPPERS] = {1, 0, 0}; // Initialize with 0 acceleration
// Variables for managing serial input and commands
volatile bool runAllowed = false;
// The received integer
volatile int receivedValue = 0;
const int ledPin = 13;
void setup() {
// Initialize I2C communication as slave
Wire.begin(0x8); // Address of the Arduino
// Setup pin 13 as output and turn LED off at the beginning
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
// Set maximum speed and acceleration for each stepper motor
for (int i = 0; i < MAX_STEPPERS; i++) {
steppers[i].setMaxSpeed(100000); // Speed = Steps / second
steppers[i].setAcceleration(800); // Acceleration = Steps /(second)^2
steppers[i].disableOutputs(); // Disable outputs initially
steppers[i].setCurrentPosition(0); // Reset current position to 0
}
home();
// Register function to run when data is received
Wire.onReceive(receiveEvent);
}
void loop() {
if (runAllowed) {
runMotors();
}
delay(100);
}
// Function to parse integer from I2C data
int parse_Integer() {
if (Wire.available() >= 2) { // We expect 2 bytes for the integer
byte byte1 = Wire.read();
byte byte2 = Wire.read();
// Combine the two bytes to form the integer
receivedValue = (byte1 << 8) | byte2;
return receivedValue;
}
return 0;
}
// Function to run when data is received
void receiveEvent(int howMany) {
digitalWrite(ledPin, '1');
for (int i = 0; i < MAX_STEPPERS; i++) {
receivedSteps[i] = parse_Integer();
receivedSpeed[i] = parse_Integer();
receivedAcceleration[i] = parse_Integer();
}
for (int i = 0; i < MAX_STEPPERS; i++) {
steppers[i].setAcceleration(receivedAcceleration[i]);
steppers[i].setMaxSpeed(receivedSpeed[i]);
steppers[i].move(receivedSteps[i]);
}
runAllowed = true;
}
void home() {
runAllowed = true;
for (int i = 0; i < MAX_STEPPERS; i++) {
receivedSteps[i] = 200;
receivedSpeed[i] = 800;
receivedAcceleration[i] = 100;
}
for (int i = 0; i < MAX_STEPPERS; i++) {
steppers[i].setAcceleration(receivedAcceleration[i]);
steppers[i].setMaxSpeed(receivedSpeed[i]);
steppers[i].move(-1 * receivedSteps[i]);
}
runMotors();
for (int i = 0; i < MAX_STEPPERS; i++) {
steppers[i].setCurrentPosition(0);
}
runAllowed = false;
}
void runMotors() {
if (runAllowed) {
while (steppers[0].currentPosition() != -1*receivedSteps[0] && steppers[1].currentPosition() != -1*receivedSteps[1] && steppers[2].currentPosition() != -1*receivedSteps[2]) {
for (int i = 0; i < MAX_STEPPERS; i++) {
steppers[i].enableOutputs(); // Enable outputs for all motors
steppers[i].run(); // Step each motor
}
// if (Serial.available() > 0) return;
}
runAllowed = false;
}
if (!runAllowed) {
for (int i = 0; i < MAX_STEPPERS; i++) {
steppers[i].disableOutputs(); // Disable outputs for all motors
}
}
}
我看到了你代码中的几个问题,这些问题可能会导致 Arduino 没有进入
receiveEvent
函数的问题,因此你的电机没有按照预期移动。让我们分解一下:
Python 端(Raspberry Pi)
-
重复的寄存器地址: 你正在使用
bus.write_i2c_block_data(addr, 0, data)
将数据发送到 Arduino。第二个参数0
代表 Arduino 上的 I2C 寄存器地址。你正在为步长、速度和加速度使用相同的地址 (0
),这会导致数据被覆盖。你应该为每个参数使用唯一的寄存器地址(例如,步长为 0,速度为 1,加速度为 2)。 -
不必要的数据转换:
struct.pack(">h", value)
方法返回一个字节数组,但你又使用list(...)
将其转换为列表。write_i2c_block_data
函数可以直接使用字节数组,因此这种转换是不必要的。
这是更正后的 Python 代码:
from smbus2 import SMBus
from time import sleep
import struct
addr = 0x8 # bus address
bus = SMBus(1) # indicates /dev/i2c-1
steps = [600, 600, 600]
speeds = [500, 500, 500]
accels = [200, 200, 200]
while True:
for i in range(3):
bus.write_i2c_block_data(addr, 0, struct.pack(">h", steps[i]))
sleep(0.001)
bus.write_i2c_block_data(addr, 1, struct.pack(">h", speeds[i]))
sleep(0.001)
bus.write_i2c_block_data(addr, 2, struct.pack(">h", accels[i]))
sleep(0.001)
sleep(2)
Arduino 端
-
数据接收逻辑: 你的
receiveEvent
函数假设它将在每次调用时接收到所有三个电机的数据。但是,Python 代码一次只为一个电机发送数据。这会导致数据解释不正确。你应该修改receiveEvent
函数来处理每个接收到的数据块,并跟踪已经接收到哪些数据。 -
parse_Integer
函数: 你正在parse_Integer
函数中读取两个字节,这很好,但你并没有跟踪已经读取了多少个字节或为哪个电机读取了数据。
以下是更新后的 Arduino 代码,它解决了这些问题:
#include <Wire.h>
#include <AccelStepper.h>
#include <cvzone.h>
// ... (电机配置与之前相同) ...
// 用于存储每个电机接收到的参数的数组
volatile long receivedSteps[MAX_STEPPERS] = {1, 0, 0}; // 初始化为 0 步
volatile long receivedSpeed[MAX_STEPPERS] = {1, 0, 0}; // 初始化为 0 速度
volatile long receivedAcceleration[MAX_STEPPERS] = {1, 0, 0}; // 初始化为 0 加速度
// 用于跟踪接收到的数据的变量
volatile byte currentMotorIndex = 0;
volatile byte dataReceivedCount = 0;
// ... (其他变量与之前相同) ...
void setup() {
// ... (初始化与之前相同) ...
// 注册当接收到数据时要运行的函数
Wire.onReceive(receiveEvent);
}
void loop() {
// ... (与之前相同) ...
}
// 当接收到数据时要运行的函数
void receiveEvent(int howMany) {
digitalWrite(ledPin, HIGH); // 打开 LED 以指示数据接收
while (Wire.available()) {
byte receivedByte = Wire.read();
if (dataReceivedCount == 0) {
// 接收到的第一个字节 - 电机索引
currentMotorIndex = receivedByte;
dataReceivedCount++;
} else if (dataReceivedCount == 1) {
// 接收到的第二个字节 - 数据高字节
receivedSteps[currentMotorIndex] = receivedByte << 8;
dataReceivedCount++;
} else if (dataReceivedCount == 2) {
// 接收到的第三个字节 - 数据低字节
receivedSteps[currentMotorIndex] |= receivedByte;
dataReceivedCount = 0; // 重置计数以接收下一个数据
// 检查是否收到了所有电机的数据
if (currentMotorIndex == MAX_STEPPERS - 1) {
// 处理接收到的数据并运行电机
for (int i = 0; i < MAX_STEPPERS; i++) {
steppers[i].setAcceleration(receivedAcceleration[i]);
steppers[i].setMaxSpeed(receivedSpeed[i]);
steppers[i].move(receivedSteps[i]);
}
runAllowed = true;
}
}
}
}
// ... (其他函数与之前相同) ...
在这个更新后的代码中,
receiveEvent
函数现在跟踪接收到的数据量以及它属于哪个电机。一旦收到了所有三个电机的数据,它就会更新电机参数并启用电机运行。
通过这些更改,你的 Python 代码和 Arduino 代码应该能够正确地通信并控制步进电机。
标签:python,arduino,raspberry-pi,i2c,smbus From: 78780445