Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: steering and braking ros nodes #24

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
Braking ROS Node (encoder_briter.py)
#####################################

This is the braking system encoder node, it is pretty simple, there is no configuration mode, you do the configuration on the fly and it can save the previous config thanks to its internal memory.
This encoder is a Briter CANbus Multi-turn Absolute Rotary Encoder with 24 revolutions, 12-bit resolution.

Datasheet/user manual here: https://briterencoder.com/wp-content/uploads/2021/09/CAN-Multi-Turns-User-manual-V2.2.pdf

File Overview
^^^^^^^^^^^^^
The file overview will explain the important parts of the node and furnish essential information about its overall function and key features

Main
^^^^^
The main function initializes the ros node and creates an instance of the Encoder which has the setup of the encoder and a publisher of the Encoder message (Encoder.msg custom ROS message)

EncoderPublisher Class
^^^^^^^^^^^^^^^^^^^^^^

This class represents an interface for the Briter encoder in ROS2, it inherits the Node class from ROS2 and initializes the following attributes:

#. id: Encoder id in hex
#. steps: steps per revolution
#. degrees: degrees
#. timer_period: period between the timer_callback executions (more about timer_callback below)

Publishers:

.. figure:: /images/ros_braking_node/braking_publisher.png
:align: center
:alt: publishers
:figclass: align-center
:width: 600px

This publisher publishes encoder data to "encoder_freno" topic and it publishes at a 10Hz rate.


CAN Interface:

.. figure:: /images/ros_braking_node/can_interface.png
:align: center
:alt: can_interface
:figclass: align-center
:width: 600px

For aditional information about the can.interface.Bus() function visit the official documentation of the used CAN library: https://python-can.readthedocs.io/en/stable/api.html

An instance of NewPrinter() class (more about this class below) is created as self.listener, the object is then passed to the can.Notifier() function in the instantiation of the self.notifier variable. The can.Notifier function is used as a message distributor for a bus. Notifier creates a thread to read messages from the bus and distributes them to listeners.

.. figure:: /images/ros_braking_node/listener_creation.png
:align: center
:alt: listener
:figclass: align-center
:width: 600px

.. figure:: /images/ros_braking_node/can_notifier.png
:align: center
:alt: notifier
:figclass: align-center
:width: 600px


After this, we have several functions for different purposes in the encoder.

==================== =========================================================== ===============================================================================================================================================================================================================
Name Arguments Functionality
==================== =========================================================== ===============================================================================================================================================================================================================
shutdown None. Calls the CAN notifier stop function.
timer_callback None. Calls the ask_position function with the self.id as the argument.
ask_position id (Encoder CAN ID) Defines a CAN message variable with the data array that gets the position from the Encoder (check the datasheet's CAN commands data table at the start of this page for more information) and then it sends the message through the CAN bus.
query_mode id (Encoder CAN ID) Sets the encoder mode to query through a can.Message variable as the function above and also sends it through the CAN bus.
cambiar_id id, new (Encoder CAN ID, desired ID) Creates the CAN message and it sends it through the bus.
cambiar_baudrate id, baud (Encoder CAN ID, desired baudrate) If the baudrate argument matches one of the baudrate options it creates the CAN message and then it sends it throught the bus, if it doesn't match any option it raises an Exception.
set_return_time id, microsegundos (Encoder CAN ID, microseconds value) Sets the automatic return time of the encoder. Not working at the moment of writing this, more info about this functionality in the datasheet.
cambiar_posicion id, pos (Encoder CAN ID, desired position) Sets the current position value of the encoder. Not working at the moment of writing this, more info about this functionality in the datasheet.
position_reset id (Encoder CAN ID) Sets the current position to zero. Creates the CAN message and then sends it through the CAN bus.
==================== =========================================================== ===============================================================================================================================================================================================================

NewPrinter Class
^^^^^^^^^^^^^^^^^

The NewPrinter() class is used to create a listener that can be called directly to handle the CAN messages sent. The NewPrinter() class implements the on_message_received function that is executed when theres a new message on the bus (visit https://python-can.readthedocs.io/en/stable/listeners.html#can.Listener for more info)

When on_message_received is executed, the data is interpreted and processed so it can be usable, the data is decoded, the absolute position is adjusted and a step count is calculated based on the number of steps. Then it initializes the message to be sent as send_msg using the Encoder() custom message. After that it calculates the angle, the absolute angle and the turn, and if the angle is more than 30 degrees it calls the position_reset function from the Encoder. The data is published to the "encoder_freno" topic mentioned previously.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Steering ROS Node (encoder_rm8004.py)
##################################

The steering node starts can communication with rm8004 encoder, starts encoder in operational mode, listens to data recieved in the bus, interprets the data, publishes encoder interpreted data to topic "ifm_encoder" and publishes the absolute steering angle to topic "sdc_state_steering"

File Overview
^^^^^^^^^^^^^
The file overview will explain the important parts of the node and furnish essential information about its overall function and key features

Main
^^^^^
The main function initializes the node and creates an instance of RM8004Encoder() which itself is an encoder and angle data publisher

RM8004Encoder Class
^^^^^^^^^^^^^^^^^^^

The RM8004Encoder Class represents an interface for the IFM RM8004 encoder in Ros2, it inherits the Node ros2 base class and initializes
the following attributes:

#. steps: steps per revolution
#. revolution: revolution per unit
#. degrees: maximum degrees
#. encoder_data: instance of Encoder(), Encoder is a custom message type that stores encoder data (see sdv_msgs/msg/Encoder.msg)
#. encoder_id: node number id, according to encoder documentation: the address of an encoder without terminal cap is set to 32 as a standard
#. bit_res: bit resolution
#. car_steering_range: steerinng angle range
#. car_steering_pos: variable that stores the steering position in encoder steps besed on the specified steering range, encoder steps in one revolution and the total angles of rotation. The result of: self.car_steering_range * self.revolutions is the amount of encoder steps that are in the entire steering angle range and that result divided by degrees results in the total amount of steps in certain degree, giving you a step "position"

Parameters:

After the initial attributes are declared, two parameters are declared: "channel" of type string and "bitrate" of type int using the declare_parameter() function and retrieving their value using the get_parameter() function. The value of "channel" and "bitrate" are declared in a yaml file (see sdv_can/config/can_params.yaml) and made accesible by putting them in the share directory of the package using get_package_share_directory() function (see sdv_can/launch/can_devices.launch.py )

Publishers:

Two important publishers are created:

.. figure:: /images/ROS_steering_node/publishers.png
:align: center
:alt: publishers
:figclass: align-center
:width: 600px

These two publishers publish encoder data to "ifm_enncoder" topic and a steerung angle to the "/sdc_state/steering" topic

Starting can communication:

.. figure:: /images/ROS_steering_node/can_com.png
:align: center
:alt: publishers
:figclass: align-center
:width: 600px

For aditional information about the can.interface.Bus(), can.Message() and bus.send() functions visit the official documentation of the used can library: https://python-can.readthedocs.io/en/stable/api.html

NewPrinter Class
^^^^^^^^^^^^^^^^^

At the end of RM8004Encoder() class an instance of NewPrinter() class is created, the object is then passed to the can.Notifier() function. The can.Notifier function is used as a message distributor for a bus. Notifier creates a thread to read messages from the bus and distributes them to listeners.

.. figure:: /images/ROS_steering_node/notifier.png
:align: center
:alt: publishers
:figclass: align-center
:width: 600px

The NewPrinter() class is used to create a listener that can be called directly to handle the CAN messages sent. The NewPrinter() class implements the on_message_recieved funtion that is executed when theres a new message on the bus (visit https://python-can.readthedocs.io/en/stable/listeners.html#can.Listener for more info)

When on_message_recieved executes the data is interpreted and processed so it can be usable, the data is decoded, converted to decimal, the absolute position is adjusted and a step count is calculated based on the number of steps. If the absolute position exceeds half of the total resolution it is adjusted so it falls
in the acceptable range. The data is published to the encoder and steering angle topics discussed previously.

.. figure:: /images/ROS_steering_node/publish_on_recieve.png
:align: center
:alt: publishers
:figclass: align-center
:width: 600px
Original file line number Diff line number Diff line change
Expand Up @@ -517,9 +517,19 @@ a maximum collector-emiter voltage of 45V.

Stepper-Based Modules
=====================

The steering and pedal brake modules share essentially the same purpose: control stepper motors and read encoder and brake signals (optional).
For this reason, the PCB for both modules is exactly the same.


.. toctree::
:maxdepth: 2
:caption: Ros Nodes For Steering and Braking

ROS_Stepper_Based_Modules/ROS_Steering
ROS_Stepper_Based_Modules/ROS_Braking


---
I/O
---
Expand Down
Binary file added source/images/ROS_steering_node/can_com.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/images/ROS_steering_node/notifier.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/images/ROS_steering_node/publishers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.