Hardware¶
The other examples use simulated hardware for simplicity and easy execution on any development system.
To be able to control real hardware we recommend to derive a Simulation
and Hardware
version from a shared interface.
Depending on your environment you can then instantiate the correct implementation without bothering with it in the rest of your code.
Custom Implementation¶
For a differential-steering controlled robot, RoSys offers a Wheels
base class plus a WheelsSimulation
.
The following example illustrates how to implement a CustomWheelsHardware
module that derives from Wheels
, reads the currrent velocity regularly and can be steered with linear and angular velocity.
#!/usr/bin/env python3
from nicegui import ui
import rosys
class CustomWheelsHardware(rosys.hardware.Wheels):
def __init__(self) -> None:
super().__init__()
rosys.on_repeat(self.read_current_velocity, 0.01)
async def drive(self, linear: float, angular: float) -> None:
await super().drive(linear, angular)
# TODO send hardware command to drive with given linear and angular velocity
async def stop(self) -> None:
await super().stop()
# TODO send hardware command to stop the wheels
async def read_current_velocity(self) -> None:
velocities: list[rosys.geometry.Velocity] = []
# TODO: read measured velocities from the hardware
self.VELOCITY_MEASURED.emit(velocities)
try:
wheels = CustomWheelsHardware()
robot = rosys.hardware.Robot([wheels])
except Exception:
wheels = rosys.hardware.WheelsSimulation()
robot = rosys.hardware.RobotSimulation([wheels])
odometer = rosys.driving.Odometer(wheels)
steerer = rosys.driving.Steerer(wheels)
rosys.driving.keyboard_control(steerer)
rosys.driving.joystick(steerer)
ui.label().bind_text_from(wheels, 'linear_target_speed', lambda l: f'Linear: {l:.2f} m/s')
ui.label().bind_text_from(wheels, 'angular_target_speed', lambda a: f'Angular: {a:.2f} rad/s')
ui.run(title='RoSys')
Depending on your hardware you may need to modify a PWM signal, send commands via CAN bus or serial, use Protobuf over Ethernet or something else. By raising an exception if the real hardware is not available, a simulated robot is instantiated instead. The robot can be controlled by keyboard or joystick.
Robot Brain¶
The Zauberzeug Robot Brain is an industrial-grade controller which combines artificial intelligence with machinery. It has a built-in ESP32 microcontroller with Lizard installed to do the actual hardware communication in realtime.
Serial communication is used to send and receive messages between the built-in NVidia Jetson and the microcontroller.
You can call SerialCommunication.is_possible()
to automatically switch between simulation and real hardware.
The module WheelsHardware
expects a RobotBrain
, which controls the SerialCommunication
with the microcontroller.
#!/usr/bin/env python3
from nicegui import ui
from rosys.driving import Odometer, Steerer, joystick, keyboard_control
from rosys.hardware import (
CanHardware,
RobotBrain,
RobotHardware,
RobotSimulation,
SerialCommunication,
WheelsHardware,
WheelsSimulation,
communication,
)
is_real = SerialCommunication.is_possible()
if is_real:
communication = SerialCommunication()
robot_brain = RobotBrain(communication)
can = CanHardware(robot_brain)
wheels = WheelsHardware(robot_brain, can=can)
robot = RobotHardware([can, wheels], robot_brain)
else:
wheels = WheelsSimulation()
robot = RobotSimulation([wheels])
odometer = Odometer(wheels)
steerer = Steerer(wheels)
keyboard_control(steerer)
joystick(steerer)
if is_real:
communication.debug_ui()
robot_brain.developer_ui()
ui.run(title='RoSys')
With communication.debug_ui()
you can add some helpful UI elements for debugging the serial communication.
Furthermore, with robot_brain.developer_ui()
you can add UI elements to configure and reboot Lizard.
The Lizard configuration for a differential-steering controlled robot with an ODrive might look as follows:
can = Can(32, 33, 1000000)
l = ODriveMotor(can, 0x000)
r = ODriveMotor(can, 0x100)
l.m_per_tick = 0.0627
r.m_per_tick = 0.0627
wheels = ODriveWheels(l, r)
wheels.width = 0.515
core.output("core.millis wheels.linear_speed:3 wheels.angular_speed:3")