OpenRE hands-on
Introduction to the code framework
OpenRE framework diagram ! picture1
The code of OpenRE is mainly divided from bottom to top into Bottom Interface Function Library, Common Peripheral Driver Library, OS Library, Abstraction Library for Robot Objects, Porting of Third Party Libraries, etc.
The following is a brief introduction to each type of library.
- Underlying interface function library
Based on the official firmware library, the operation of the underlying resources such as GPIO, timer, encoder, USART, CAN, IIC, SPI, ADC and PWM will be further encapsulated. - Commonly used peripheral driver library
Based on the official firmware library, drivers are provided for commonly used peripherals, such as motor control, servo control, LCD screen driver, GPS module decoding, decoding of airplane remote control and data processing of IMU, etc. - OS library
The developed application contains bare runner version and OS version, the library contains the relevant files of ucos OS. - Abstraction library for robot objects
Abstraction of the mobile chassis model, including differential chassis, 4-wheel McWheel chassis and 3-wheel omnidirectional wheel chassis, etc. - Porting of third-party libraries
Ported third-party libraries, including Dobot robot arm library, Eigen3 and Matrix matrix library.
Porting of third-party master control
OpenRE is an open source robotics embedded system. In order to make it available to more robotics enthusiasts, we present here a tutorial on porting OpenRE to third-party masters (non-HandsFree masters).
Many robotics enthusiasts have this question when they come across OpenRE: I have my own robot, I have my own embedded master, but I want to use OpenRE for development, what should I do? After everything is debugged smoothly, refer to the tutorial Introduction to Architecture - Writing Robot Programs Based on OpenRE and HandsFree Master to get OpenRE running on your own robot.
Next, let's start the third-party master porting journey.
As you should have learned in the Introduction to the code framework section, OpenRE code can be divided into four parts according to the folder arrangement: project files, master description files, function package files, operating system files and third-party library files.
- Operating system files are for the Ucos operating system.
- Third-party library files are libraries that have been written by others when we implement some specific functions in our project.
We can put these two files aside for now. Of the three remaining files.
- The master description file is the lowest level and contains all the function implementations related to the lowest level operation of the board. These underlying driver functions will be called in a large number of project files and function package files.
In other words, when we want to port OpenRE to our own board, we just need to finish writing the master description file so that the API exposed to the public is the same as the original one, and then we don't need to make any changes to the project folder and function package folder, and OpenRE will be successfully ported to the third-party master.
Next, let's dissect the master description file (1_Processor) in detail, which contains three main parts.
BoardAbstract:
The board_abstract class is the top-level board abstract class, which leaves all the API interfaces for board operations, but considering that different boards have different hardware designs, so in some places where they may differ, we use dummy functions, and then use the corresponding board for each board subclasses of each board to inherit the board_abstract class and will be a concrete implementation of the virtual function.
Each board will have a control_unit_xxx.cpp (that is, where the board subclasses are implemented concretely) corresponding to it, and these cpp files contain led initialization, buzzer initialization, motor interface initialization, motor enable disable, ADC initialization, etc.. We can find that these operations need to correspond with the specific hardware design of the board, such as LED specific corresponding to which IO port, which varies from board to board, so we need to correspond to the implementation in the subclass.
It is worth noting that different control_unit_xxx.cpp are using the same board.h header file, which ensures the consistency of the external API interface, as to which control_unit_xxx.cpp is associated with board.h in a project, depending on the writing of board.mk, interested can scroll up to 4.2Makefile for more details.
Interupt.
This section is the description of the interrupt handling functions, which should not need to be modified.
6 serial interrupt functions (5 in the case of F1) constantly pressing the data received by the serial port into their respective queues.
a main interrupt function, which is constantly timing for the dividing operation in while(1).
An exception interrupt, where the microcontroller is alerted after an exception occurs using a buzzer.
STM32F4/STM32F1.
This makes our well-written BSPLIB, which packages various underlying functions of the microcontroller for implementation and can be called directly.
Since the resources of the IO of the determined model of microcontroller are fixed, for example, the serial port 1 of F1 has always been PA9, PA10 without remapping, so this part of the file also does not need to be changed.
In summary, the process of porting OpenRE to a third-party master is as follows.
- Create a new control_unit_xxx.cpp (xxx is the name of your board).
- Imitate the existing control_unit_v2.cpp and implement all functions according to the characteristics of your board. Do not change the function names, but correspond to the board.h. 3.
- modify board.mk, add your board information as follows.
ifeq "$(strip $(BOARD_TYPE))" "control_unit_xxx"
DDEFS += -DCONTROL_UNIT_XXX -DSTM32F10X
DDEFS += -DHSE_VALUE=8000000 -DUSE_STDPERIPH_DRIVER
MCU ? = cortex-m3
CPU_TYPE ? = STM32F1
BOARD_ABSTRACT += $(TOP_PATH)/1_Processor/BoardAbstract/control_unit_xxx.cpp
endif
- where, MCU, CPU_TYPE and "-DSTM32F10X" need to be changed according to the chip you are using. *4.
- Add your board's hardware resource allocation information in the system_para.mk file.
ifeq "$(strip $(BOARD_TYPE))" "control_unit_xxx"
DEBUG_PRINTF_INTERFACE ? = usart_interface_4
PC_INTERFACE ? = usart_interface_1
RADIO_INTERFACE ? = usart_interface_4
endif
- The three parameters are, debug communication port, pc side and master communication port, remote control communication port. You just need to fill in according to your board's hardware resource allocation. *.
- modify the first line inside the Makefile of the project folder: BOARD_TYPE ? = control_unit_xxx
Important function package explanation
In this summary, we will single out a few more important software packages to do a more detailed explanation, so that you can understand and learn how to implement some important functions.
common.
This package contains two files, one is the printf remapping of the debug interface, which is used to print the printf information to the serial debug assistant. There is also a queue implementation for receiving messages queue.cpp. We create a queue for each serial port for receiving messages and the data sent is pressed into the queue in the serial port interrupt handler function. In other processes of the program, the data in the queue is read for processing.
hf_link:
Since hf_link is more tedious, we put it in chapter 6 to explain systematically.
motor.
This function package contains the motor control code motor_control and motor_top.
The most important thing in motor_control is the pidOrdinaryCall function and the pidSeriesCall function.
We also created the motor_control class, which implements the general PID control functions, but also leaves the specific board design related to the motor initialization, enable and other virtual function interface, in its subclass DC_motor class in the specific implementation.
- pidOrdinaryCall: This function is a single-stage PID function with input parameters including, in order, the desired total mileage, the actual measured total mileage, the desired mileage per unit time and the actual measured mileage per unit time. The output of the function is then the PWM value provided to the motor.
- PidSeriesCall: string-level PID, interested students can look at the source code to pick up a pick, we are currently using a single-stage PID, the effect can already reach the control requirements.
- motor_top contains the motor initialization function, control callback function and motor test function. The DC_motor class inherits from the motor_control class and implements the virtual functions in the motor_control class. The motor_top class creates a group of all motors used by the robot to manage all motors in a unified way.
- motorTopInit: motor initialization function, input includes the number of motors, PID control period, motor parameters and simulation mode enable.
- motorTopCall: This function will be called in While(1) with a fixed frequency to realize the control of all motors in the robot chassis.
- motorTest: This function is usually used only during motor debugging, in other words for testing motors by internal staff during development. The way to use it is to put it together with motorTopCall and block out the robot_control_p->call(); function inside main.c.
robot_control.
This function package contains four parts, which are the robot's arm, the chassis, the gimbal (head) and the general control of the robot.
Chassis (chassis) part, build a chassis class, mainly to achieve the chassis parameters set, the chassis movement of the relevant data to solve the calculation.
- setParameters: set the parameters of the chassis, including the model and size of the chassis. Here is a very key idea, we defined a parent class TF_robot class, to achieve the conversion of robot speed and motor speed, for different chassis structure, there are different subclasses to inherit the parent class, the speed conversion function of the parent class instantiation. So here we construct a parent class object, and then point to different subclasses according to the robot's chassis model to realize the code encapsulation.
- Init: initializes the chassis, which actually calls the above function.
- updataGlobalSpeed: Decomposes the global speed of the robot into the speed of each wheel.
- updataRobotSpeed: Decomposes the robot's coordinate system speed into the speed of each wheel.
- call: chassis control callback function, decode the mileage from the encoder data.
In the robot_control (overall robot control) section, we implement a robot control class that is oriented to the whole robot, and is responsible for the response of hf_link while coordinating the control of the arm, head and chassis. The most important functions are call and hfLinkNodeEvent.
- call: the robot control callback function, which internally runs the hf_link response function and the control callback functions for the three parts of the robot (arm, head, and chassis). This function is called with a fixed frequency in while(1) to achieve control of the robot.
- hfLinkNodeEvent: hf_link response function. When hf_link updates the robot with some data or parameters, some response is needed. For example, after hf_link updates the global coordinate speed of the robot, the receive_package_renew[SET_GLOBAL_SPEED] flag bit will be set to 1. Once the change of this bit is detected, chassis.updataGlobalSpeed() will be called to decode the global speed into the speed of each motor. to realize the robot's movement.