New
#1601
do you follow pimoroni on twitter ?
https://twitter.com/pimoroni
do you follow pimoroni on twitter ?
https://twitter.com/pimoroni
POE for a Pi has been around for a while. The 3B and 4B have that option. It can be expensive to implement though. The POE Hats plus a special POE switch.
I saw that new doable NVMe base. One isuue is you can't boot from either one of the NVMe drives. Not with the current Pi 5 firmware anyway.
All I've been doing is trying to read the motor RPM from the Motor Encoders. The read_encoders.py file works, and both encoders show the position from 0 to 360 degrees. So they must be working? Code wise anything else doesn't seem to work though? I have a thread started on the Pimoroni forum. The one who knows this stuff is currently on vacation. One thing that hinders me, at my python skill level; is the examples are over complicated. They do too many things at once. They are great demos of the device, but understanding what's going on in the file can be challenging for beginners. Or even moderate skill users.
I have one of these on the way, VL53L5CX 8x8 Time of Flight (ToF) Array Sensor Breakout I'm going to use it for collision avoidance.
Can you post your code? Maybe I can spot where you are having a problem. After all, I am eventually going to have to do this too with my little robot car. I don't mind looking if you want me to.
Gets me this:Code:import time import math from inventor import Inventor2040W, MOTOR_A, MOTOR_B, NUM_LEDS from pimoroni import PID, NORMAL_DIR, REVERSED_DIR from encoder import Encoder, MMME_CPR from encoder import MMME_CPR HALF_LEDS = NUM_LEDS / 2 GEAR_RATIO = 110 SPEED_SCALE = 6.0 UPDATES = 100 # How many times to update the motor per second UPDATE_RATE = 1 / UPDATES TIME_FOR_EACH_MOVE = 1 # The time to travel between each random value, in seconds UPDATES_PER_MOVE = TIME_FOR_EACH_MOVE * UPDATES PRINT_DIVIDER = 4 # How many of the updates should be printed (i.e. 2 would be every other update) # Multipliers for the different printed values, so they appear nicely on the Thonny plotter ACC_PRINT_SCALE = 0.05 SPD_PRINT_SCALE = 40 # Acceleration multiplier POSITION_EXTENT = 180 # How far from zero to move the motor, in degrees MAX_SPEED = 6.0 # The maximum speed to move the motor at, in revolutions per second INTERP_MODE = 0 # PID values POS_KP = 0.025 # Position proportional (P) gain POS_KI = 0.0 # Position integral (I) gain POS_KD = 0.0 # Position derivative (D) gain VEL_KP = 30.0 # Velocity proportional (P) gain VEL_KI = 0.0 # Velocity integral (I) gain VEL_KD = 0.4 # Velocity derivative (D) gain pos_pid = PID(POS_KP, POS_KI, POS_KD, UPDATE_RATE) vel_pid = PID(VEL_KP, VEL_KI, VEL_KD, UPDATE_RATE) board = Inventor2040W(motor_gear_ratio=GEAR_RATIO) LEFT = board.motors[MOTOR_A] LEFT.direction(REVERSED_DIR) LEFT.speed_scale(SPEED_SCALE) LEFT_enc = board.encoders[0] LEFT_enc.direction(REVERSED_DIR) RIGHT = board.motors[MOTOR_B] RIGHT.direction(NORMAL_DIR) RIGHT.speed_scale(SPEED_SCALE) RIGHT_enc = board.encoders[1] RIGHT_enc.direction(NORMAL_DIR) LEFT.enable() RIGHT.enable() time.sleep(1) # Drive at full positive LEFT.speed(6) RIGHT.speed(6.0) board.leds.clear() for L in range(LEFT.speed()): board.leds.set_rgb(L, 0, 255, 0) for R in range(6, RIGHT.speed() + 6): board.leds.set_rgb(R, 0, 255, 0) time.sleep(15) capture_LEFT = LEFT_enc.capture() capture_RIGHT = RIGHT_enc.capture() capture_LEFT capture_RIGHT print("LEFT Speed =", LEFT.speed()) print("LEFT RPM =", capture_LEFT .revolutions_per_minute) print("RIGHT Speed =", RIGHT.speed()) print("RIGHT RPM =", capture_RIGHT.revolutions_per_minute) accel = vel_pid.calculate(capture_LEFT.revolutions_per_second) print("Vel =", capture_RIGHT.revolutions_per_second, end=", ") print("Vel SP =", vel_pid.setpoint, end=", ") print("Accel =", accel * ACC_PRINT_SCALE, end=", ") print("Speed =", LEFT.speed()) time.sleep(5)# Access the motor from Inventor and enable it # Stop moving LEFT.stop() RIGHT.stop() board.leds.clear() for L in range(LEFT.speed()): board.leds.set_rgb(L, 0, 255, 0) for R in range(6, RIGHT.speed() + 6): board.leds.set_rgb(R, 0, 255, 0) time.sleep(15) capture_LEFT = LEFT_enc.capture() capture_RIGHT = RIGHT_enc.capture() capture_LEFT capture_RIGHT print("LEFT Speed =", LEFT.speed()) print("LEFT RPM =", capture_LEFT .revolutions_per_minute) print("RIGHT Speed =", RIGHT.speed()) print("RIGHT RPM =", capture_RIGHT .revolutions_per_minute) print("Vel =", capture_LEFT.revolutions_per_second, end=", ") print("Vel SP =", vel_pid.setpoint, end=", ") print("Accel =", accel * ACC_PRINT_SCALE, end=", ") print("Speed =", LEFT.speed()) time.sleep(5)# Access the motor from Inventor and enable it board.leds.clear() RIGHT.speed(3.0) LEFT.speed(3.0) board.leds.clear() for L in range(RIGHT.speed()): board.leds.set_rgb(L, 0, 255, 0) for R in range(6, LEFT.speed() + 6): board.leds.set_rgb(R, 0, 255, 0) time.sleep(15) capture_LEFT = LEFT_enc.capture() capture_RIGHT = RIGHT_enc.capture() capture_LEFT capture_RIGHT print("LEFT Speed =", LEFT.speed()) print("LEFT RPM =", capture_LEFT .revolutions_per_minute) print("RIGHT Speed =", RIGHT.speed()) print("RIGHT RPM =", capture_RIGHT .revolutions_per_minute) print("Vel =", capture_LEFT.revolutions_per_second, end=", ") print("Vel SP =", vel_pid.setpoint, end=", ") print("Accel =", accel * ACC_PRINT_SCALE, end=", ") print("Speed =", LEFT.speed()) time.sleep(5) board.leds.clear() # Stop moving RIGHT.stop() LEFT.stop() for L in range(LEFT.speed()): board.leds.set_rgb(L, 0, 255, 0) for R in range(6, RIGHT.speed() + 6): board.leds.set_rgb(R, 0, 255, 0) time.sleep(15) capture_LEFT = LEFT_enc.capture() capture_RIGHT = RIGHT_enc.capture() capture_LEFT capture_RIGHT print("LEFT Speed =", LEFT.speed()) print("LEFT RPM =", capture_LEFT .revolutions_per_minute) print("Right Speed =", RIGHT.speed()) print("RIGHT RPM =", capture_RIGHT .revolutions_per_minute) print("Vel =", capture_LEFT.revolutions_per_second, end=", ") print("Vel SP =", vel_pid.setpoint, end=", ") print("Accel =", accel * ACC_PRINT_SCALE, end=", ") print("Speed =", LEFT.speed()) time.sleep(5) # Disable the motor LEFT.disable() RIGHT.disable()
I get different numbers each time I run it, so I have it messed up pretty good.Code:>>> %Run -c $EDITOR_CONTENT MPY: soft reboot LEFT Speed = 6.0 LEFT RPM = -102.5264 RIGHT Speed = 6.0 RIGHT RPM = -101.9872 Vel = -1.699787, Vel SP = 0, Accel = 5.980709, Speed = 6.0 LEFT Speed = 0.0 LEFT RPM = -130.1845 RIGHT Speed = 0.0 RIGHT RPM = -130.1866 Vel = -2.169742, Vel SP = 0, Accel = 5.980709, Speed = 0.0 LEFT Speed = 3.0 LEFT RPM = -27.2943 RIGHT Speed = 3.0 RIGHT RPM = -26.96078 Vel = -0.454905, Vel SP = 0, Accel = 5.980709, Speed = 3.0 LEFT Speed = 0.0 LEFT RPM = -63.22392 Right Speed = 0.0 RIGHT RPM = -62.75304 Vel = -1.053732, Vel SP = 0, Accel = 5.980709, Speed = 0.0 >>>
pimoroni-pico/micropython/modules_py/inventor.py at main . pimoroni/pimoroni-pico . GitHub
pimoroni-pico/micropython/modules/encoder at main . pimoroni/pimoroni-pico . GitHub
@Catnip. I can control the motor speed no problem, near as I can tell anyway. They look to be turning in the correct direction and the correct speed. Where things fail is trying to read the rpm etc from the encoders. The files I'm looking at for code are here.
pimoroni-pico/micropython/examples/inventor2040w/motors at main . pimoroni/pimoroni-pico . GitHub
This works, it shows the correct orientation as I turn the wheels.
pimoroni-pico/micropython/examples/inventor2040w/read_encoders.py at main . pimoroni/pimoroni-pico . GitHub
Other examples don't work though, and do odd things etc, but don't throw up any error messages?
[QUOTE=alphanumeric;2618860]Gets me this:Code:import time import math from inventor import Inventor2040W, MOTOR_A, MOTOR_B, NUM_LEDS from pimoroni import PID, NORMAL_DIR, REVERSED_DIR from encoder import Encoder, MMME_CPR from encoder import MMME_CPR HALF_LEDS = NUM_LEDS / 2 GEAR_RATIO = 110 SPEED_SCALE = 6.0 UPDATES = 100 # How many times to update the motor per second UPDATE_RATE = 1 / UPDATES TIME_FOR_EACH_MOVE = 1 # The time to travel between each random value, in seconds UPDATES_PER_MOVE = TIME_FOR_EACH_MOVE * UPDATES PRINT_DIVIDER = 4 # How many of the updates should be printed (i.e. 2 would be every other update) # Multipliers for the different printed values, so they appear nicely on the Thonny plotter ACC_PRINT_SCALE = 0.05 SPD_PRINT_SCALE = 40 # Acceleration multiplier POSITION_EXTENT = 180 # How far from zero to move the motor, in degrees MAX_SPEED = 6.0 # The maximum speed to move the motor at, in revolutions per second INTERP_MODE = 0 # PID values POS_KP = 0.025 # Position proportional (P) gain POS_KI = 0.0 # Position integral (I) gain POS_KD = 0.0 # Position derivative (D) gain VEL_KP = 30.0 # Velocity proportional (P) gain VEL_KI = 0.0 # Velocity integral (I) gain VEL_KD = 0.4 # Velocity derivative (D) gain pos_pid = PID(POS_KP, POS_KI, POS_KD, UPDATE_RATE) vel_pid = PID(VEL_KP, VEL_KI, VEL_KD, UPDATE_RATE) board = Inventor2040W(motor_gear_ratio=GEAR_RATIO) LEFT = board.motors[MOTOR_A] LEFT.direction(REVERSED_DIR) LEFT.speed_scale(SPEED_SCALE) LEFT_enc = board.encoders[0] LEFT_enc.direction(REVERSED_DIR) RIGHT = board.motors[MOTOR_B] RIGHT.direction(NORMAL_DIR) RIGHT.speed_scale(SPEED_SCALE) RIGHT_enc = board.encoders[1] RIGHT_enc.direction(NORMAL_DIR) LEFT.enable() RIGHT.enable() time.sleep(1) # Drive at full positive LEFT.speed(6) RIGHT.speed(6.0) board.leds.clear() for L in range(LEFT.speed()): board.leds.set_rgb(L, 0, 255, 0) for R in range(6, RIGHT.speed() + 6): board.leds.set_rgb(R, 0, 255, 0) time.sleep(15) capture_LEFT = LEFT_enc.capture() capture_RIGHT = RIGHT_enc.capture() capture_LEFT capture_RIGHT print("LEFT Speed =", LEFT.speed()) print("LEFT RPM =", capture_LEFT .revolutions_per_minute) White space violation? print("RIGHT Speed =", RIGHT.speed()) print("RIGHT RPM =", capture_RIGHT.revolutions_per_minute) accel = vel_pid.calculate(capture_LEFT.revolutions_per_second) print("Vel =", capture_RIGHT.revolutions_per_second, end=", ") print("Vel SP =", vel_pid.setpoint, end=", ") print("Accel =", accel * ACC_PRINT_SCALE, end=", ") print("Speed =", LEFT.speed()) time.sleep(5)# Access the motor from Inventor and enable it # Stop moving LEFT.stop() RIGHT.stop() board.leds.clear() for L in range(LEFT.speed()): board.leds.set_rgb(L, 0, 255, 0) for R in range(6, RIGHT.speed() + 6): board.leds.set_rgb(R, 0, 255, 0) time.sleep(15) capture_LEFT = LEFT_enc.capture() capture_RIGHT = RIGHT_enc.capture() capture_LEFT capture_RIGHT print("LEFT Speed =", LEFT.speed()) print("LEFT RPM =", capture_LEFT .revolutions_per_minute) White space violation? print("RIGHT Speed =", RIGHT.speed()) print("RIGHT RPM =", capture_RIGHT .revolutions_per_minute) White space violation? print("Vel =", capture_LEFT.revolutions_per_second, end=", ") print("Vel SP =", vel_pid.setpoint, end=", ") print("Accel =", accel * ACC_PRINT_SCALE, end=", ") print("Speed =", LEFT.speed()) time.sleep(5)# Access the motor from Inventor and enable it board.leds.clear() RIGHT.speed(3.0) LEFT.speed(3.0) board.leds.clear() for L in range(RIGHT.speed()): board.leds.set_rgb(L, 0, 255, 0) for R in range(6, LEFT.speed() + 6): board.leds.set_rgb(R, 0, 255, 0) time.sleep(15) capture_LEFT = LEFT_enc.capture() capture_RIGHT = RIGHT_enc.capture() capture_LEFT capture_RIGHT print("LEFT Speed =", LEFT.speed()) print("LEFT RPM =", capture_LEFT .revolutions_per_minute) White space violation? print("RIGHT Speed =", RIGHT.speed()) print("RIGHT RPM =", capture_RIGHT .revolutions_per_minute) White space violation? print("Vel =", capture_LEFT.revolutions_per_second, end=", ") print("Vel SP =", vel_pid.setpoint, end=", ") print("Accel =", accel * ACC_PRINT_SCALE, end=", ") print("Speed =", LEFT.speed()) time.sleep(5) board.leds.clear() # Stop moving RIGHT.stop() LEFT.stop() for L in range(LEFT.speed()): board.leds.set_rgb(L, 0, 255, 0) for R in range(6, RIGHT.speed() + 6): board.leds.set_rgb(R, 0, 255, 0) time.sleep(15) capture_LEFT = LEFT_enc.capture() capture_RIGHT = RIGHT_enc.capture() capture_LEFT capture_RIGHT print("LEFT Speed =", LEFT.speed()) print("LEFT RPM =", capture_LEFT .revolutions_per_minute) White space violation? print("Right Speed =", RIGHT.speed()) print("RIGHT RPM =", capture_RIGHT .revolutions_per_minute) White space violation? print("Vel =", capture_LEFT.revolutions_per_second, end=", ") print("Vel SP =", vel_pid.setpoint, end=", ") print("Accel =", accel * ACC_PRINT_SCALE, end=", ") print("Speed =", LEFT.speed()) time.sleep(5) # Disable the motor LEFT.disable() RIGHT.disable()
Thanks for posting the code. I don't see anything immediately wrong with this, but I suspect that the "revolutions per minute" method is what is giving you trouble. For one thing, your RPM is negative (or did you want that?). Also, there is a random function in the "rev per minute" method that may be setting the start point to a random number, as near as I can tell. I also highlighted what I think are whitespace violations in the above code. Have a look and see if you agree. I have bolded my comments.Code:>>> %Run -c $EDITOR_CONTENT MPY: soft reboot LEFT Speed = 6.0 LEFT RPM = -102.5264 RIGHT Speed = 6.0 RIGHT RPM = -101.9872 Vel = -1.699787, Vel SP = 0, Accel = 5.980709, Speed = 6.0 LEFT Speed = 0.0 LEFT RPM = -130.1845 RIGHT Speed = 0.0 RIGHT RPM = -130.1866 Vel = -2.169742, Vel SP = 0, Accel = 5.980709, Speed = 0.0 LEFT Speed = 3.0 LEFT RPM = -27.2943 RIGHT Speed = 3.0 RIGHT RPM = -26.96078 Vel = -0.454905, Vel SP = 0, Accel = 5.980709, Speed = 3.0 LEFT Speed = 0.0 LEFT RPM = -63.22392 Right Speed = 0.0 RIGHT RPM = -62.75304 Vel = -1.053732, Vel SP = 0, Accel = 5.980709, Speed = 0.0 >>>
I will write this in pseudocode, but this is more or less what I would do:
I hope the above makes sense. My python is not so good (improving, but not so good) but to me, that is the easy way. The examples use trigonometry to work out the RPM. You could use trig to be more precise, but it is a bit more involved and I am not sure how to do it without a setup here of my own to experiment on. I have some encoder sensors coming from DigiKey, but it will be a little bit before I get them set up.Code:# set up everything as you have (motor speed, direction, etc.) # verify the angular distance in degrees for the encoder wheel that you are using (I assume that it is optical) # How many slots are in the encoder wheel? Divide 360 by that number. That is your angular distance in degrees # make a constant to hold the zero point of the encoder wheel. If it gets to 360 degrees, it must be set back to zero for the next set of counts. 360 and zero are the same point on the wheel. # make the code able to differentiate between forward and reverse by using positive numbers for forward and negative numbers for reverse. The RPM function has to be able to deal with those negative numbers #then: angular_distance = whatever the angular distance is slot_count = the number of slots in the wheel, this becomes counts per rev revolutions = holder for the revolutions count (this is a variable) pulses = the pulses from the encoder wheel (variable is used here) def getRPM(which motor, motor speed, direction... etc...) while() # make a loop in python that will go on until you decide to stop it for testing purposes # you can use a timer here to measure the time in one revolution # start timer pulses += GetPulse(encoder) if pulses == slot_count: #stop timer RPM = 60 seconds / timer_value #this gives your approximate RPM pulses = 0 # reset the pulse count here #reset timer #print the RPM somewhere # if the method falls through to here it means that the pulse count does not equal a full revolution and will go through the loop again until it gets a full revolution
Anyway, mess around with my pseudocode and see if you can get an actual python method to count the RPMs properly. Let me know how it goes and I will think about this some more.
Good luck.
The -rpm just means it thinks the motors are running in reverse. You can set the speed from -6 to +6. The default value is -1 to +1, if you don't set a speed scale. I want to use 6 RGB LED's for each motor as RPM indicators. So I set my speed scale to 6. I may go back to 1 for further testing though. I can reverse the encoder direction to get + numbers. I have the left motor set to reverse direction, and should be doing the same for it's encoder. Thanks for the code, will be tinkering with this over the weekend.