机器人上电后,左轮不受控制地转动
-
几天没用机器人,今天一上电,发现左轮不受控制转动。后测试发现,如果烧录fishbot驱动程序后,好像是在(micro-ros) 建立连接前,左轮会一直转,但是连接后都正常了(左轮不转了);但如果烧进去的程序没有占用电机供电接口线路(我猜的),比如说一个单纯控制舵机的程序,那么左轮会一直转;
亲测如果有外设占用了这个5V接口,(没使用micro-ros,所以连接快),那么左轮也会停止转动(马上的,但是像受到了压制似的会颤抖) (如下图,我这里接了一个语音模块)
除此之外,烧录进去的程序好像都是能正常运行的(比如说ADC,舵机什么的),就是左轮会在空闲时不停地转
求小鱼老师@小鱼 和大佬们帮忙看看,是不是板子哪里短路了 -
@Gerry 板子的硬件版本号,目前来看有两个可能
- 你接的5V电源导致小车本身电源不够了(小车的5V信号输出能力并不强,本身功耗大)
- 你占用了相关的引脚导致有问题,看看原理图是否有冲突
-
根据你的描述,这个问题看起来比较复杂,涉及到多个可能的原因。下面我会列举一些可能的原因和相应的排查步骤,希望能帮助你找到问题所在。
可能的原因及排查步骤
-
硬件连接问题
- 检查电机驱动接口:确保左轮电机的接口连接正确,没有松动或接错线的情况。
- 检查电源线路:检查给电机供电的5V线路是否有短路或断路的情况。可以用万用表测量线路的通断和电压。
-
程序控制问题
- 分析驱动程序:你提到在烧录fishbot驱动程序后,左轮在建立连接前会转动,但连接后正常。这可能是驱动程序在初始化阶段对电机有不当的控制。检查驱动程序中是否有初始化代码导致电机转动。
- 测试不同程序:你提到在没有占用电机供电接口的程序下,左轮会一直转。这可能是因为在这些程序中,没有正确配置或禁用电机控制相关的引脚。尝试在这些程序中添加代码来明确设置电机控制引脚的状态(如高阻态或低电平)。
-
电源管理问题
- 检查电源稳定性:确保机器人的电源供应稳定,没有电压波动或电源不足的情况。
- 分析外设影响:你提到当有其他外设占用5V接口时,左轮会停止转动。这可能是电源分配或电流限制的问题。检查电源管理模块或稳压器是否能提供足够的电流来满足所有外设的需求。
-
电磁干扰问题
- 检查电磁环境:有时候电磁干扰也会导致电机不受控制地转动。检查机器人周围是否有强电磁场或高频信号源。
- 添加滤波措施:在电源线路或电机控制线路上添加滤波器或去耦电容,以减少电磁干扰的影响。
-
硬件故障
- 检查电机和驱动模块:如果以上步骤都没有发现问题,可能是电机或驱动模块本身出现故障。尝试更换电机或驱动模块进行测试。
建议的排查顺序
- 重新检查硬件连接:确保所有连接都正确无误。
- 分析并修改程序:在程序中明确设置电机控制引脚的状态。
- 测试电源管理:检查电源供应和分配情况。
- 考虑电磁干扰:添加滤波措施并检查电磁环境。
- 更换硬件测试:如果以上步骤都无效,考虑更换可能故障的硬件。
希望这些建议能帮助你找到问题所在并解决它!如果还有其他疑问或需要进一步的帮助,请随时提问。
以上为生成回答,仅供参考~
-
-
@Gerry
感觉是IMU的I2C总线那边的电路出问题了,我又将整车驱动烧录进去,然后ros2 topic echo /imu时发现数据永远为零;于是我又将以前的教授IMU的两节的代码烧录进去(example06_mpu6050 和 example07_mpu6050_oop 的代码),果然报错了,如下图,图一是 example06_mpu6050 的报错,图二是example07_mpu6050_oop 的报错(都是 returned error263):
(图一)
(图二) -
@Gerry 硬件版本号还有代码发一下
-
@小鱼
自己的整车驱动代码:#include <Arduino.h> #include <micro_ros_platformio.h> #include <WiFi.h> #include <rcl/rcl.h> #include <rclc/rclc.h> #include <rclc/executor.h> #include <geometry_msgs/msg/twist.h> #include <Esp32PcntEncoder.h> // 包含用于计数电机编码器脉冲的 ESP32 PCNT 编码器库 #include <Esp32McpwmMotor.h> // 包含使用 ESP32 的 MCPWM 硬件模块控制 DC 电机的 ESP32 MCPWM 电机库 #include <PidController.h> // 包含 PID 控制器库,用于实现 PID 控制 #include <Kinematics.h> // 运动学相关实现 #include "ImuDriver.h" #include <nav_msgs/msg/odometry.h> #include "sensor_msgs/msg/imu.h" #include <micro_ros_utilities/string_utilities.h> #include <fishlog.h> #define RCSOFTCHECK(fn) \ { \ rcl_ret_t temp_rc = fn; \ if ((temp_rc != RCL_RET_OK)) \ { \ fishlog_debug("ros2", \ "Failed status on line %d: %d. Continuing.\n", __LINE__, (int)temp_rc); \ } \ } sensor_msgs__msg__Imu imu_msg; rcl_publisher_t imu_publisher; MPU6050 mpu(Wire); // 初始化MPU6050对象 ImuDriver imu(mpu); // 初始化Imu对象 imu_t imu_data; // IMU 数据对象 rcl_publisher_t odom_publisher; // 用于发布机器人的里程计信息(Odom) nav_msgs__msg__Odometry odom_msg; // 机器人的里程计信息 rcl_timer_t timer; Esp32PcntEncoder encoders[2]; // 编码器数组 rclc_executor_t executor; rclc_support_t support; rcl_allocator_t allocator; rcl_node_t node; rcl_subscription_t subscriber; geometry_msgs__msg__Twist sub_msg; Esp32McpwmMotor motor; float out_motor_speed[2]; // 用于保存输出电机速度 PidController pid_controller[2]; // 创建PidController的两个对象 Kinematics kinematics; // 运动学相关对象 const unsigned int timer_timeout = 200; void twist_callback(const void *msg_in) { const geometry_msgs__msg__Twist *twist_msg = (const geometry_msgs__msg__Twist *)msg_in; static float target_motor_speed1, target_motor_speed2; float linear_x = twist_msg->linear.x; // 获取 Twist 消息的线性 x 分量 float angular_z = twist_msg->angular.z; // 获取 Twist 消息的角度 z 分量 Serial.printf("linear speed: %f angular speed: %f\n", linear_x, angular_z); kinematics.kinematic_inverse(linear_x * 1000, angular_z, target_motor_speed1, target_motor_speed2); pid_controller[0].update_target(target_motor_speed1); pid_controller[1].update_target(target_motor_speed2); } void callback_sensor_publisher_timer_(rcl_timer_t *timer, int64_t last_call_time) { RCLC_UNUSED(last_call_time); if (timer != NULL) { // 用于获取当前的时间戳,并将其存储在消息的头部中 int64_t stamp = rmw_uros_epoch_millis(); // 获取机器人的位置和速度信息,并将其存储在一个ROS消息(odom_msg)中 odom_t odom = kinematics.odom(); odom_msg.header.stamp.sec = static_cast<int32_t>(stamp / 1000); // 秒部分 odom_msg.header.stamp.nanosec = static_cast<uint32_t>((stamp % 1000) * 1e6); // 纳秒部分 odom_msg.pose.pose.position.x = odom.x; odom_msg.pose.pose.position.y = odom.y; odom_msg.pose.pose.orientation.w = odom.quaternion.w; odom_msg.pose.pose.orientation.x = odom.quaternion.x; odom_msg.pose.pose.orientation.y = odom.quaternion.y; odom_msg.pose.pose.orientation.z = odom.quaternion.z; odom_msg.twist.twist.angular.z = odom.angular_speed; odom_msg.twist.twist.linear.x = odom.linear_speed; Serial.printf("X: %f Y: %f LINEAR: %f ANGULAR: %f\n", odom.x, odom.y, odom.linear_speed, odom.angular_speed); if(rcl_publish(&odom_publisher, &odom_msg, NULL) != RCL_RET_OK) { Serial.printf("Failed to publish: %s\n", rcl_get_error_string().str); } if (imu.isEnable()) { imu.getImuDriverData(imu_data); imu_msg.header.stamp.sec = static_cast<int32_t>(stamp / 1000); // 秒部分 imu_msg.header.stamp.nanosec = static_cast<uint32_t>((stamp % 1000) * 1e6); // 纳秒部分 imu_msg.angular_velocity.x = imu_data.angular_velocity.x; imu_msg.angular_velocity.y = imu_data.angular_velocity.y; imu_msg.angular_velocity.z = imu_data.angular_velocity.z; imu_msg.linear_acceleration.x = imu_data.linear_acceleration.x; imu_msg.linear_acceleration.y = imu_data.linear_acceleration.y; imu_msg.linear_acceleration.z = imu_data.linear_acceleration.z; imu_msg.orientation.x = imu_data.orientation.x; imu_msg.orientation.y = imu_data.orientation.y; imu_msg.orientation.z = imu_data.orientation.z; imu_msg.orientation.w = imu_data.orientation.w; rcl_publish(&imu_publisher, &imu_msg, NULL); } } } // 这个函数是一个后台任务,负责设置和处理与 micro-ROS 代理的通信。 void microros_task(void *param) { IPAddress agent_ip; agent_ip.fromString("192.168.31.5"); set_microros_wifi_transports("1013dormitory", "19101693727", agent_ip, 8888); // 使用 micro_ros_string_utilities_set 函数设置到 odom_msg.header.frame_id 中 odom_msg.header.frame_id = micro_ros_string_utilities_set(odom_msg.header.frame_id, "odom"); odom_msg.child_frame_id = micro_ros_string_utilities_set(odom_msg.child_frame_id, "base_link"); delay(2000); allocator = rcl_get_default_allocator(); RCSOFTCHECK(rclc_support_init(&support, 0, NULL, &allocator)); RCSOFTCHECK(rclc_node_init_default(&node, "esp32_car", "", &support)); RCSOFTCHECK(rclc_subscription_init_best_effort( &subscriber, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(geometry_msgs, msg, Twist), "/cmd_vel")); rcl_ret_t pub_init_ret = rclc_publisher_init_best_effort( &odom_publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(nav_msgs, msg, Odometry), "odom"); if (pub_init_ret != RCL_RET_OK) { Serial.printf("Failed to initialize odom_publisher! Error code: %d, Error message: %s\n", pub_init_ret, rcl_get_error_string().str); rcl_reset_error(); // 清除错误,以便后续不受影响 } rcl_ret_t pub_init_ret1 = rclc_publisher_init_best_effort( &imu_publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(sensor_msgs, msg, Imu), "imu"); if (pub_init_ret1 != RCL_RET_OK) { Serial.printf("Failed to initialize odom_publisher! Error code: %d, Error message: %s\n", pub_init_ret, rcl_get_error_string().str); rcl_reset_error(); // 清除错误,以便后续不受影响 } RCSOFTCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator)); RCSOFTCHECK(rclc_executor_add_subscription(&executor, &subscriber, &sub_msg, &twist_callback, ON_NEW_DATA)); // 调用 rclc_timer_init_default 函数初始化 ROS 2 定时器,传入支持库、定时器周期和回调函数 // rclc_timer_init_default(&timer, &support, RCL_MS_TO_NS(timer_timeout), callback_sensor_publisher_timer_); // 调用 rclc_executor_add_timer 函数将定时器添加到执行器中,传入执行器和定时器。 // rclc_executor_add_timer(&executor, &timer); // 循环运行 micro-ROS 执行器以处理传入的消息。 while (true) { if (!rmw_uros_epoch_synchronized()) { rmw_uros_sync_session(1000); // 如果时间同步成功,则将当前时间设置为MicroROS代理的时间,并输出调试信息。 delay(10); } // Serial.printf("11111\n"); delay(50); RCSOFTCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100))); if(rcl_publish(&odom_publisher, &odom_msg, NULL) != RCL_RET_OK) { Serial.printf("Failed to publish: %s\n", rcl_get_error_string().str); } if(rcl_publish(&imu_publisher, &imu_msg, NULL) != RCL_RET_OK) { Serial.printf("Failed to publish: %s\n", rcl_get_error_string().str); } } } void setup() { Serial.begin(115200); imu.begin(18, 19); motor.attachMotor(0, 22, 23); motor.attachMotor(1, 12, 13); // 在引脚32、33和26、25上初始化两个编码器 encoders[0].init(0, 32, 33); encoders[1].init(1, 26, 25); // 初始化PID控制器的kp、ki和kd pid_controller[0].update_pid(0.625, 0.125, 0.0); pid_controller[1].update_pid(0.625, 0.125, 0.0); // 初始化PID控制器的最大输入输出,MPCNT大小范围在正负100之间 pid_controller[0].out_limit(-100, 100); pid_controller[1].out_limit(-100, 100); // 设置运动学参数 kinematics.set_motor_param(0, 45, 44, 65); kinematics.set_motor_param(1, 45, 44, 65); kinematics.set_kinematic_param(150); // 在核心0上创建一个名为"microros_task"的任务,栈大小为10240 xTaskCreatePinnedToCore(microros_task, "microros_task", 10240, NULL, 1, NULL, 0); //delay(4000); } unsigned long previousMillis = 0; unsigned long interval = 50; void loop() { static float out_motor_speed[2]; kinematics.update_motor_ticks(micros(), encoders[0].getTicks(), encoders[1].getTicks()); if(pid_controller[0].target_ == 0 && pid_controller[1].target_ == 0) { out_motor_speed[0] = 0; out_motor_speed[1] = 0; } else { out_motor_speed[0] = pid_controller[0].update(kinematics.motor_speed(0)); out_motor_speed[1] = pid_controller[1].update(kinematics.motor_speed(1)); } motor.updateMotorSpeed(0, out_motor_speed[0]); motor.updateMotorSpeed(1, out_motor_speed[1]); unsigned long currentMillis = millis(); // 获取当前时间 if (currentMillis - previousMillis >= interval) { // 判断是否到达间隔时间 previousMillis = currentMillis; // 记录上一次打印的时间 float linear_speed, angle_speed; kinematics.kinematic_forward(kinematics.motor_speed(0), kinematics.motor_speed(1), linear_speed, angle_speed); //RCSOFTCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100))); // Serial.printf("[%ld] linear:%f angular:%f\n", currentMillis, linear_speed, angle_speed); // Serial.printf("[%ld] x:%f y:%f yaml:%f\n", currentMillis, // kinematics.odom().x, kinematics.odom().y, kinematics.odom().yaw); // 用于获取当前的时间戳,并将其存储在消息的头部中 int64_t stamp = rmw_uros_epoch_millis(); // 获取机器人的位置和速度信息,并将其存储在一个ROS消息(odom_msg)中 odom_t odom = kinematics.odom(); odom_msg.header.stamp.sec = static_cast<int32_t>(stamp / 1000); // 秒部分 odom_msg.header.stamp.nanosec = static_cast<uint32_t>((stamp % 1000) * 1e6); // 纳秒部分 odom_msg.pose.pose.position.x = odom.x; odom_msg.pose.pose.position.y = odom.y; odom_msg.pose.pose.orientation.w = odom.quaternion.w; odom_msg.pose.pose.orientation.x = odom.quaternion.x; odom_msg.pose.pose.orientation.y = odom.quaternion.y; odom_msg.pose.pose.orientation.z = odom.quaternion.z; odom_msg.twist.twist.angular.z = odom.angular_speed; odom_msg.twist.twist.linear.x = odom.linear_speed; //Serial.printf("X: %f Y: %f LINEAR: %f ANGULAR: %f\n", odom.x, odom.y, odom.linear_speed, odom.angular_speed); // if(rcl_publish(&odom_publisher, &odom_msg, NULL) != RCL_RET_OK) // { // Serial.printf("Failed to publish: %s\n", rcl_get_error_string().str); // } if (imu.isEnable()) { imu.getImuDriverData(imu_data); imu_msg.header.stamp.sec = static_cast<int32_t>(stamp / 1000); // 秒部分 imu_msg.header.stamp.nanosec = static_cast<uint32_t>((stamp % 1000) * 1e6); // 纳秒部分 imu_msg.angular_velocity.x = imu_data.angular_velocity.x; imu_msg.angular_velocity.y = imu_data.angular_velocity.y; imu_msg.angular_velocity.z = imu_data.angular_velocity.z; imu_msg.linear_acceleration.x = imu_data.linear_acceleration.x; imu_msg.linear_acceleration.y = imu_data.linear_acceleration.y; imu_msg.linear_acceleration.z = imu_data.linear_acceleration.z; imu_msg.orientation.x = imu_data.orientation.x; imu_msg.orientation.y = imu_data.orientation.y; imu_msg.orientation.z = imu_data.orientation.z; imu_msg.orientation.w = imu_data.orientation.w; // 打印线性加速度 Serial.printf("Linear Acceleration: %.3f, %.3f, %.3f\n", imu_msg.linear_acceleration.x, imu_msg.linear_acceleration.y, imu_msg.linear_acceleration.z); // 打印方向 Serial.printf("Orientation: %.3f, %.3f, %.3f, %.3f\n", imu_msg.orientation.x, imu_msg.orientation.y, imu_msg.orientation.z, imu_msg.orientation.w); // if(rcl_publish(&imu_publisher, &imu_msg, NULL) != RCL_RET_OK) // { // Serial.printf("Failed to publish: %s\n", rcl_get_error_string().str); // } } } // 延迟10毫秒 delay(10); }
自己外接的DFPlayer的代码:
#include "Arduino.h" #include "SoftwareSerial.h" #include "DFRobotDFPlayerMini.h" SoftwareSerial mySoftwareSerial(22, 23); // RX, TX DFRobotDFPlayerMini myDFPlayer; void printDetail(uint8_t type, int value); void setup() { mySoftwareSerial.begin(9600); Serial.begin(115200); Serial.println(); Serial.println(F("DFRobot DFPlayer Mini Demo")); Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)")); if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3. Serial.println(F("Unable to begin:")); Serial.println(F("1.Please recheck the connection!")); Serial.println(F("2.Please insert the SD card!")); while(true){ delay(0); // Code to compatible with ESP8266 watch dog. } } Serial.println(F("DFPlayer Mini online.")); myDFPlayer.volume(25); //Set volume value. From 0 to 30 myDFPlayer.play(1); //Play the first mp3 } void loop() { static unsigned long timer = millis(); if (millis() - timer > 2000) { timer = millis(); myDFPlayer.next(); //Play next mp3 every 3 second. Serial.println(F("Playing Next one")); } if (myDFPlayer.available()) { printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states. } } void printDetail(uint8_t type, int value){ switch (type) { case TimeOut: Serial.println(F("Time Out!")); break; case WrongStack: Serial.println(F("Stack Wrong!")); break; case DFPlayerCardInserted: Serial.println(F("Card Inserted!")); break; case DFPlayerCardRemoved: Serial.println(F("Card Removed!")); break; case DFPlayerCardOnline: Serial.println(F("Card Online!")); break; case DFPlayerUSBInserted: Serial.println("USB Inserted!"); break; case DFPlayerUSBRemoved: Serial.println("USB Removed!"); break; case DFPlayerPlayFinished: Serial.print(F("Number:")); Serial.print(value); Serial.println(F(" Play Finished!")); break; case DFPlayerError: Serial.print(F("DFPlayerError:")); switch (value) { case Busy: Serial.println(F("Card not found")); break; case Sleeping: Serial.println(F("Sleeping")); break; case SerialWrongStack: Serial.println(F("Get Wrong Stack")); break; case CheckSumNotMatch: Serial.println(F("Check Sum Not Match")); break; case FileIndexOut: Serial.println(F("File Index Out of Bound")); break; case FileMismatch: Serial.println(F("Cannot Find File")); break; case Advertise: Serial.println(F("In Advertise")); break; default: break; } break; default: break; } }
其余的代码(ADC,mpu6050的代码和教程是一样的)
-
@小鱼 硬件版本号指什么?没看懂,就用的原车自带的ROS开发板啊
-
@Gerry 板子的硬件版本号,目前来看有两个可能
- 你接的5V电源导致小车本身电源不够了(小车的5V信号输出能力并不强,本身功耗大)
- 你占用了相关的引脚导致有问题,看看原理图是否有冲突
-
@小鱼 应该是tfplayer的问题,去掉了就好了,等我再看看
-