02 Python Chassis Motion Control
Python Chassis Motion Control
In this chapter, we'll provide a Python demo for controlling the motion of a robot's chassis. This approach can be adapted to other programming languages for similar motion control tasks.
Control Mechanism Overview
We utilize code blocks within JupyterLab to compose JSON commands. These commands are then dispatched to the microcontroller via the GPIO serial port on a Raspberry Pi (the default baud rate for communication with the sub-controller is 115200). Upon receiving these commands, the microcontroller executes the specified actions.
Further sections will delve into the variety of commands that can be sent to the sub-controller. Alternatively, you might choose to implement these functionalities in a different programming language or develop a dedicated application for the controlling system.
Design Advantages
Adopting a dual-controller architecture significantly liberates the resources of the master device. In this setup, the host controller (such as a Raspberry Pi or Jetson SBC) acts as the "brain", while the ESP32 sub-controller serves as a "cerebellum-like" entity handling the lower-level motion controls. This division of tasks allows the host to focus on high-level tasks like vision processing and decision-making, while the sub-controller efficiently manages precise movement control and other low-level tasks. Such an arrangement ensures that the sub-controller can maintain accurate wheel rotation speeds through high-frequency PID control, without overburdening the host with computationally intensive tasks.
Main Program (app.py)
The main program of the product, app.py, automatically executes upon booting due to the configuration set by autorun.sh (which is pre-configured in the product). Running app.py occupies the GPIO serial port and the camera, which might lead to conflicts or errors if other tutorials or programs require these resources. Ensure to disable the auto-start of app.py before proceeding with development or learning.
As app.py employs multithreading and uses crontab for autostart, typical commands like sudo killall python won't suffice to terminate it. You would need to comment out the relevant line in crontab and reboot the device.
crontab -e
Upon your first use of this command, you will be prompted to select an editor to open the crontab file. It is recommended to choose "nano" by entering the corresponding number for nano, followed by pressing the Enter key to confirm.
Use the "#" symbol to comment out the line containing "...... app.py".
# @reboot ~/ugv_pt_rpi/ugv-env/bin/python ~/ugv_pt_rpi/app.py >> ~/ugv.log 2>&1 @reboot /bin/bash ~/ugv_pt_rpi/start_jupyter.sh >> ~/jupyter_log.log 2>&1
Note: Make sure not to comment out the line containing "start_jupyter.sh", as doing so will prevent JupyterLab from launching on startup, disabling your access to interactive tutorials.
To exit and save the changes, after editing the content of crontab, press Ctrl + X to exit nano. Since you have made edits to the crontab file, it will ask if you want to save the changes (Save modified buffer?). Enter the letter 'Y' and then press Enter to exit and save the modifications.
After restarting the device, the main program will no longer run automatically upon boot-up, allowing you to freely use the tutorials within JupyterLab. If you wish to re-enable the automatic execution of the main program at startup in the future, you can reopen the crontab file using the method described above. Simply remove the "#" symbol in front of the '@' symbol, then exit and save the changes. This will restore the automatic startup functionality of the main program.
Chassis Control Demo
In the following demo, we use the is_raspberry_pi5() function to determine the model of the Raspberry Pi since the device names for the GPIO serial port differ between Raspberry Pi 4B and Raspberry Pi 5. It's crucial to use the correct device name and baud rate (default 115200) that matches the sub-controller.
Before executing the code block below, ensure the robot is elevated so that the drive wheels are off the ground. Activating the code will cause the robot to move; take precautions to prevent it from falling off the table.
from base_ctrl import BaseController import time # Function for Detecting Raspberry Pi def is_raspberry_pi5(): with open('/proc/cpuinfo', 'r') as file: for line in file: if 'Model' in line: if 'Raspberry Pi 5' in line: return True else: return False # Determine the GPIO Serial Device Name Based on the Raspberry Pi Model if is_raspberry_pi5(): base = BaseController('/dev/ttyAMA0', 115200) else: base = BaseController('/dev/serial0', 115200) # The wheel rotates at a speed of 0.2 meters per second and stops after 2 seconds. base.send_command({"T":1,"L":0.2,"R":0.2}) time.sleep(2) base.send_command({"T":1,"L":0,"R":0})
By invoking the code block mentioned above, the Raspberry Pi will initially send the command {"T":1,"L":0.2,"R":0.2} (the structure of commands will be discussed in more detail in later chapters). This command starts the wheels turning. After a two-second interval, the Raspberry Pi will send another command {"T":1,"L":0,"R":0}, causing the wheels to stop. It's important to note that even if the command to stop the wheels isn't sent, the wheels will still cease turning if no new commands are issued. This is due to a heartbeat function built into the sub-controller. The purpose of this heartbeat function is to halt the current motion command automatically if the host controller hasn't sent any new commands to the sub-controller for an extended period. This function is designed to prevent continuous motion of the sub-controller in case the host encounters a problem that leads to a freeze or crash.
If you want the robot to continue moving indefinitely, the master control unit needs to cyclically send motion control commands every 2 to 4 seconds.
Chassis Model Selection
You may find that the direction or speed of the robot's wheels does not match your expectations after inputting the motion control commands. This could be because, before controlling the chassis, you need to set the type of chassis. Each time you run the main demo (app.py), the chassis type is automatically configured, and the configuration parameters are stored in config.yaml. This ensures that the chassis is controlled according to the correct parameters. By default, the chassis needs to be configured once by the host computer every time it is powered on. You can send the following commands to the chassis to set its type:
"main" value means the chassis type:
- 1: RaspRover (4-wheel 4WD chassis)
- 2: UGV Rover (6-wheel 4WD chassis)
- 3: UGV Beast (off-road tracked chassis)
"module" value means the module type:
- 0: can be set whether install the pan-tile module or not
- 1: robotic arm module (if there is not a robotic arm installed, report errors when the chassis Ping fails.)
- 2: pan-tilt module (if there is not a pan-tilt module installed, report errors when the chassis Ping fails.)
For example, if you use the UGV Rover with the pan-tilt module, you can send the following commands to the slave computer to set the chassis type:
{"T":900,"main":2,"module":0} or {"T":900,"main":2,"module":2}
- If you don't require chassis feedback on the pan-tilt's angle information (intended for user secondary development, not included in the demos), it's recommended to use the former one.
You can modify the JSON parameter to set the chassis model in the following demo block according to your products:
base.send_command({"T":900,"main":2,"module":0})
Then the following code block is executed to control the chassis, and the wheels turn in the right direction and at the right speed.
base.send_command({"T":1,"L":0.2,"R":0.2}) time.sleep(2) base.send_command({"T":1,"L":0,"R":0})
Chassis Steering Principle
The earlier demo allows you to make the robot move forward for two seconds before stopping. Further adjustments to the parameters can control the direction of the chassis, based on the differential steering principle.
When turning, the inner wheels (on the side towards which the turn is made) travel a shorter distance and thus should rotate slower to maintain stability. The differential gear system achieves this by allowing the drive wheels to rotate at different speeds. Usually, the outer wheels (on the opposite side of the turn) rotate faster than the inner wheels. This variation in speed results in the turning motion of the vehicle, allowing it to steer in the intended direction.
You can control the vehicle's steering by assigning different target linear velocities to each wheel, thus achieving maneuverability and easily adjustable turning radii.