Skip to content

error about rpi5 #40

@ncnynl

Description

@ncnynl

When using a Raspberry Pi 5 instead of Zero, two errors may occur:

  1. GPIO error. The Raspberry Pi 5 no longer controls GPIO directly from the CPU; instead, it uses a separate RP1 I/O chip.

  2. Real-time error:

Policy control budget exceeded by 0.01

Policy control budget exceeded by 0.013

Policy control budget exceeded by 0.002

Policy control budget exceeded by 0.035

Policy control budget exceeded by 0.007

Below are some solutions we have tested:

  1. Add an intermediate package, define the same GPIO handling function, and call different handling functions depending on the board.

Raspberry Pi 5 uses:

from gpiozero import LED, Button, AngularServo
from signal import signal, SIGTERM, SIGINT

# Keep track of all devices to clean up on exit
_all_devices = []

#--------------------------
#DigitalOutput
#--------------------------
classDigitalOut: 
def __init__(self, pin): 
self.dev = LED(pin) 
self.dev.off() 
_all_devices.append(self.dev) 

def on(self): 
self.dev.on() 

def off(self): 
self.dev.off()


#--------------------------
#DigitalInput
#--------------------------
classDigitalIn: 
def __init__(self, pin): 
self.dev = Button(pin, pull_up=True) 
_all_devices.append(self.dev) 

@property 
def is_active(self): 
return self.dev.is_pressed


#--------------------------
# Servo / PWM Output for Antennas
#--------------------------
classPWMOut: 

def __init__(self, pin, frequency=50, min_pulse=0.0005, max_pulse=0.0025): 
# frequency ignored for compatibility 
self.dev = AngularServo( 
pin, 
min_angle=0, 
max_angle=180, 
min_pulse_width=min_pulse, 
max_pulse_width=max_pulse 
) 
self.current_angle = 90 # start at middle 
self.dev.angle = self.current_angle 
_all_devices.append(self.dev) 

def set_duty(self, duty_percent): 

duty_percent = max(0, min(duty_percent, 100)) 
angle = duty_percent * 180 / 100 
self.dev.angle = angle 
self.current_angle = angle 

def stop(self): 
self.dev.detach()


#--------------------------
#Cleanup
#--------------------------
def cleanup(): 
for dev in _all_devices: 
if hasattr(dev, "off"): 
dev.off() 
if hasattr(dev, "close"): 
dev.close() 
if hasattr(dev, "detach"): 
dev.detach() 
_all_devices.clear()


# automatically cleanup on SIGINT or SIGTERM
def _signal_handler(signum, frame): 
cleanup() 
exit(0)

signal(SIGINT, _signal_handler)
signal(SIGTERM, _signal_handler)

For use outside Raspberry Pi 5:

import RPi.GPIO as GPIO

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)


classDigitalOut: 
def __init__(self, pin): 
self.pin = pin 
GPIO.setup(pin, GPIO.OUT) 
GPIO.output(pin, GPIO.LOW) 

def on(self): 
GPIO.output(self.pin, GPIO.HIGH) 

def off(self): 
GPIO.output(self.pin, GPIO.LOW)


classDigitalIn: 
def __init__(self, pin): 
self.pin = pin 
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) 

@property 
def is_active(self): 
return GPIO.input(self.pin) == GPIO.LOW
  1. Real-time processing
    Modify v2_rl_walk_mujoco.py
def run(self): 
i = 0 
last_t = time.perf_counter() # <<< CHANGED >>> 

try: 
print("Starting") 
while True: 
now = time.perf_counter() # <<< CHANGED >>> 
dt = now - last_t # <<< CHANGED >>> 
last_t = now # <<< CHANGED >>> 
dt = min(dt, 1.5 / self.control_freq) # <<< CHANGED >>> 

if self.commands: 
self.last_commands, self.buttons, lt, rt = ( 
self.ps4_controller.get_last_command() 
) 

if self.buttons.LB.is_pressed: 
self.phase_frequency_factor = 1.3 
else: 
self.phase_frequency_factor = 1.0 

if self.buttons.X.triggered and self.duck_config.projector: 
self.projector.switch() 

if self.buttons.B.triggered and self.duck_config.speaker: 
self.sounds.play_random_sound() 

if self.duck_config.antennas: 
self.antennas.set_position_left(lt) 
self.antennas.set_position_right(rt) 

if self.buttons.A.triggered: 
self.paused = not self.paused 
print("PAUSE" if self.paused else "UNPAUSE") 

if self.paused: 
time.sleep(0.05) 
continue 

obs = self.get_obs() 
if obs is None: 
continue 

phase_speed = ( 
self.phase_frequency_factor + self.phase_frequency_factor_offset 
) 

self.imitation_i += dt * self.control_freq * phase_speed # <<< CHANGED >>> 
self.imitation_i %= self.PRM.nb_steps_in_period # <<< CHANGED >>> 

phase = self.imitation_i / self.PRM.nb_steps_in_period * 2 * np.pi 
self.imitation_phase = np.array([np.cos(phase), np.sin(phase)]) 

action = self.policy.infer(obs) 

self.last_last_last_action = self.last_last_action.copy() 
self.last_last_action = self.last_action.copy() 
self.last_action = action.copy() 

self.motor_targets = self.init_pos + action * self.action_scale 

if self.action_filter is not None: 
self.action_filter.push(self.motor_targets) self.motor_targets = self.action_filter.get_filtered_action() 

head_motor_targets = self.last_commands[3:] + self.motor_targets[5:9] 
self.motor_targets[5:9] = head_motor_targets 

action_dict = make_action_dict( 
self.motor_targets, list(self.hwi.joints.keys()) 
) 
self.hwi.set_position_all(action_dict) 

#print("Real loop Hz:", round(1 / dt, 1)) # <<< CHANGED >>> 

except KeyboardInterrupt: 
if self.duck_config.antennas: 
self.antennas.stop() 
print("TURNING OFF")

After the above changes, the mechanical duck's behavior is basically the same as the previous version. However, there may be better ways to handle this. Hopefully, other users have also made changes, and I would appreciate any feedback.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions