ODrive driver for ros2_control

Overview

odrive_ros2_control

ENGLISH / 中文

Introduction

ODrive driver for ros2_control

Prerequisites

  • ROS Foxy
  • ODrive Firmware v0.5.3

Documentation

Done

  • Support native protocol on USB
  • Support position, speed, torque commands
  • Support position, speed, torque feedbacks
  • Support using multiple ODrives
  • Support smooth switching of control modes
  • Unit conversion adheres to REP-103
  • Support using any or both of axes on each ODrive
  • Allow multiple axes running in different control modes
  • Provide sensor data (error, voltage, temperature)
  • Auto watchdog feeding

Todo

  • Support serial port and CAN
  • Support feedforward control inputs
  • Automatic configuration of ODrives based on URDF and YAML files
Comments
  • Could not contact service /controller_manager/list_controllers

    Could not contact service /controller_manager/list_controllers

    I'm getting some weird errors while launching, errors mention "Could not contact service /controller_manager/list_controllers".

    ➜  ros2_ws ros2 launch odrive_bringup odrive.launch.py              
    [INFO] [launch]: All log files can be found below /home/richard/.ros/log/2021-09-15-15-08-53-286219-richard-GL65-9SEK-10069
    [INFO] [launch]: Default logging verbosity is set to INFO
    [INFO] [ros2_control_node-1]: process started with pid [10073]
    [INFO] [robot_state_publisher-2]: process started with pid [10075]
    [INFO] [spawner.py-3]: process started with pid [10077]
    [INFO] [spawner.py-4]: process started with pid [10079]
    [robot_state_publisher-2] Parsing robot urdf xml string.
    [robot_state_publisher-2] Link link0 had 0 children
    [robot_state_publisher-2] [INFO] [1631711333.470852643] [robot_state_publisher]: got segment link0
    [robot_state_publisher-2] [INFO] [1631711333.470911564] [robot_state_publisher]: got segment world
    [spawner.py-3] Traceback (most recent call last):
    [spawner.py-3]   File "/opt/ros/foxy/lib/controller_manager/spawner.py", line 186, in <module>
    [spawner.py-3]     sys.exit(main())
    [spawner.py-3]   File "/opt/ros/foxy/lib/controller_manager/spawner.py", line 109, in main
    [spawner.py-3]     if is_controller_loaded(node, controller_manager_name, controller_name):
    [spawner.py-3]   File "/opt/ros/foxy/lib/controller_manager/spawner.py", line 51, in is_controller_loaded
    [spawner.py-3]     controllers = list_controllers(node, controller_manager).controller
    [spawner.py-3]   File "/opt/ros/foxy/lib/python3.8/site-packages/controller_manager/controller_manager_services.py", line 49, in list_controllers
    [spawner.py-3]     return service_caller(node, f'{controller_manager_name}/list_controllers',
    [spawner.py-3]   File "/opt/ros/foxy/lib/python3.8/site-packages/controller_manager/controller_manager_services.py", line 29, in service_caller
    [spawner.py-3]     raise RuntimeError(f'Could not contact service {service_name}')
    [spawner.py-3] RuntimeError: Could not contact service /controller_manager/list_controllers
    [spawner.py-4] Traceback (most recent call last):
    [spawner.py-4]   File "/opt/ros/foxy/lib/controller_manager/spawner.py", line 186, in <module>
    [spawner.py-4]     sys.exit(main())
    [spawner.py-4]   File "/opt/ros/foxy/lib/controller_manager/spawner.py", line 109, in main
    [spawner.py-4]     if is_controller_loaded(node, controller_manager_name, controller_name):
    [spawner.py-4]   File "/opt/ros/foxy/lib/controller_manager/spawner.py", line 51, in is_controller_loaded
    [spawner.py-4]     controllers = list_controllers(node, controller_manager).controller
    [spawner.py-4]   File "/opt/ros/foxy/lib/python3.8/site-packages/controller_manager/controller_manager_services.py", line 49, in list_controllers
    [spawner.py-4]     return service_caller(node, f'{controller_manager_name}/list_controllers',
    [spawner.py-4]   File "/opt/ros/foxy/lib/python3.8/site-packages/controller_manager/controller_manager_services.py", line 29, in service_caller
    [spawner.py-4]     raise RuntimeError(f'Could not contact service {service_name}')
    [spawner.py-4] RuntimeError: Could not contact service /controller_manager/list_controllers
    [ERROR] [spawner.py-4]: process has died [pid 10079, exit code 1, cmd '/opt/ros/foxy/lib/controller_manager/spawner.py joint0_velocity_controller -c /controller_manager --ros-args'].
    [ERROR] [spawner.py-3]: process has died [pid 10077, exit code 1, cmd '/opt/ros/foxy/lib/controller_manager/spawner.py joint_state_broadcaster --controller-manager /controller_manager --ros-args'].
    

    Looking at discussions about this error I found a discussion talking about the stability of the spawn.py of the controller_manager ros-controls/ros2_control#475

    They mention it could be because it's running on a slower PC but I'm running the same code on raspberry pi and my laptop PC with powerful specs that shouldn't be a problem and yet both platforms yield the same result.

    I've also tried the fix that they proposed by incrementing the wait time but it just won't work. Any idea?

    opened by Richard-Haes-Ellis 15
  • migrating to humble from foxy

    migrating to humble from foxy

    I'm trying to move over to humble, and I've managed to get all the return types fixed (as seen in #11) - I made 2 check functions, not sure that's the best way to go about it..

    However, now I'm getting this error: image

    https://github.com/MSDRobotics/odrive_ros2_control/tree/devel/odrive_hardware_interface

    Anyone have any ideas?

    opened by ryanpennings 5
  • Improve documentation

    Improve documentation

    Hey, seems to be a nice library, trying to use it to control a differential drive robot but not entirely sure how to use it.

    Managed to compile it and launch it with

    os2 launch odrive_bringup odrive.launch.py enable_joint1:=true

    Also managed to subscribe to the joint states topic.

    ros2 topic echo /dynamic_joint_states

    How would I go about controlling the motor velocities etc? Some documentation about using the library and some examples would help the package :)

    opened by kallaspriit 4
  • y have a problem when try use diff drive controller

    y have a problem when try use diff drive controller

    Hello everyone, I am trying to make a differential robot, with odrive I currently install version 0.5.3 because 0.5.4 did not work with their library, but I was able to try the example they have without problems. but when trying to change and use diff_drive_controller I always get the same:

    [INFO] [launch]: Default logging verbosity is set to INFO
    [INFO] [ros2_control_node-1]: process started with pid [22716]
    [INFO] [robot_state_publisher-2]: process started with pid [22718]
    [INFO] [spawner.py-3]: process started with pid [22720]
    [INFO] [spawner.py-4]: process started with pid [22722]
    [robot_state_publisher-2] Parsing robot urdf xml string.
    [robot_state_publisher-2] Link base_link had 4 children
    [robot_state_publisher-2] Link caster_frontal_wheel had 0 children
    [robot_state_publisher-2] Link caster_rear_wheel had 0 children
    [robot_state_publisher-2] Link left_wheel had 0 children
    [robot_state_publisher-2] Link right_wheel had 0 children
    [robot_state_publisher-2] [INFO] [1648330252.914258134] [robot_state_publisher]: got segment base_link
    [robot_state_publisher-2] [INFO] [1648330252.914436006] [robot_state_publisher]: got segment caster_frontal_wheel
    [robot_state_publisher-2] [INFO] [1648330252.914466840] [robot_state_publisher]: got segment caster_rear_wheel
    [robot_state_publisher-2] [INFO] [1648330252.914489942] [robot_state_publisher]: got segment dummy
    [robot_state_publisher-2] [INFO] [1648330252.914506869] [robot_state_publisher]: got segment left_wheel
    [robot_state_publisher-2] [INFO] [1648330252.914523963] [robot_state_publisher]: got segment right_wheel
    [ros2_control_node-1] terminate called after throwing an instance of 'std::out_of_range'
    [ros2_control_node-1]   what():  _Map_base::at
    [spawner.py-3] [INFO] [1648330253.238620636] [spawner_joint_state_broadcaster]: Waiting for /controller_manager services
    [spawner.py-4] [INFO] [1648330253.382559544] [spawner_mk2_base_controller]: Waiting for /controller_manager services
    [ERROR] [ros2_control_node-1]: process has died [pid 22716, exit code -6, cmd '/home/paloverde/mk2_ros2/install/controller_manager/lib/controller_manager/ros2_control_node --ros-args --params-file /tmp/launch_params_k2w8y3jv --params-file /home/paloverde/mk2_p2/install/odrive_bringup/share/odrive_bringup/config/odrive_diff__controllers.yaml'].
    [spawner.py-3] [INFO] [1648330255.258554065] [spawner_joint_state_broadcaster]: Waiting for /controller_manager services
    [spawner.py-4] [INFO] [1648330255.403404351] [spawner_mk2_base_controller]: Waiting for /controller_manager services
    [spawner.py-3] [INFO] [1648330257.279708970] [spawner_joint_state_broadcaster]: Waiting for /controller_manager services
    [spawner.py-4] [INFO] [1648330257.424464528] [spawner_mk2_base_controller]: Waiting for /controller_manager services
    [spawner.py-3] [INFO] [1648330259.301082063] [spawner_joint_state_broadcaster]: Waiting for /controller_manager services
    [spawner.py-4] [INFO] [1648330259.446024642] [spawner_mk2_base_controller]: Waiting for /controller_manager services
    [spawner.py-3] [INFO] [1648330261.322128338] [spawner_joint_state_broadcaster]: Waiting for /controller_manager services
    [spawner.py-4] [INFO] [1648330261.466963012] [spawner_mk2_base_controller]: Waiting for /controller_manager services
    [spawner.py-3] [ERROR] [1648330263.343010495] [spawner_joint_state_broadcaster]: Controller manager not available
    [ERROR] [spawner.py-3]: process has died [pid 22720, exit code 1, cmd '/home/paloverde/mk2_ros2/install/controller_manager/lib/controller_manager/spawner.py joint_state_broadcaster --ros-args'].
    [spawner.py-4] [ERROR] [1648330263.487450622] [spawner_mk2_base_controller]: Controller manager not available
    [ERROR] [spawner.py-4]: process has died [pid 22722, exit code 1, cmd '/home/paloverde/mk2_ros2/install/controller_manager/lib/controller_manager/spawner.py mk2_base_controller --ros-args'].
    ^C[WARNING] [launch]: user interrupted with ctrl-c (SIGINT)
    [robot_state_publisher-2] [INFO] [1648330266.007189717] [rclcpp]: signal_handler(signal_value=2)
    [INFO] [robot_state_publisher-2]: process has finished cleanly [pid 22718]
    

    also use the example from: rosbot with the same result.

    Has anyone had a similar problem? Or do you know what could be causing this problem?

    thanks for you help

    opened by Gilocho 3
  • migrating from Foxy to galactic

    migrating from Foxy to galactic

    thank you for your contribution.

    I'm using ros2 galactic, and trying to use your lib. but when I try to build the package, I got Screenshot from 2022-03-12 12-34-12 so I tried to migrate it to galactic as mentioned here in ros2-conttrol framework. this is the package after editing according to the ros2-control migration document, but I got this error Screenshot from 2022-03-13 18-37-24

    I'm knew to programming and couldn't solve it, do you have any idea how to solve it?

    opened by HusamZain 3
  • Reset speed at startup

    Reset speed at startup

    When I kill the odrive_ros2_control node while the motor speeds are not zero, the motors nicely stop because of the watchdog. But the next time I start the process again, the motors begin to spin at the last set speed during startup until a new speed command is received (which is not great for safety).

    I'm guessing that at startup the library starts to feed the watchdog so last used speed is restored before zero speed is set again?

    Could you set the requested speed to zero before starting the watchdog?

    opened by kallaspriit 3
  • USB communication hangs on launch

    USB communication hangs on launch

    When starting odrive.launch.py, a 'RuntimeError: Could not contact service /controller_manager/list_controllers' is encounterd. Using the debugger it seems that the USB communication is the problem: the second libusb_bulk_transfer (ODRIVE_IN_ENDPOINT) hangs. Any suggestions?

    using the odrivetool utility usb communication works as expected

    opened by ardwesterveld 1
  • migirate from foxy to galactic for ros2-control

    migirate from foxy to galactic for ros2-control

    Hei ! thank you for your work ! I'm trying to migrate your odrive-ros2-control package from Foxy to galactic as mention here in ros2-control documentation. but I got this error error which I hope you can help me with Screenshot from 2022-03-13 18-37-24

    do you have any idea how to solve it ? thanks in advance

    opened by HusamZain 1
  • Takes about 5ms for a read/write

    Takes about 5ms for a read/write

    I measured the communication time and found that an RW operation for a single axis takes about 5ms, which is super inefficient. It can only support one odrive with 2 axises running at 100hz. May be use asynchronous API of libusb can speed up the communication and make it possible for multiple odrive running at high frequency

    enhancement 
    opened by CaptainKAZ 1
  • fail to run controller_manager

    fail to run controller_manager

    THIS IS my controller.yaml file

    controller_manager:
      ros__parameters:
        update_rate: 100 # Hz
    
        joint_state_broadcaster:
          type: joint_state_broadcaster/JointStateBroadcaster
    
        joint0_position_controller:
          type: position_controllers/JointGroupPositionController
    
        joint0_velocity_controller:
          type: velocity_controllers/JointGroupVelocityController
    
        joint0_effort_controller:
          type: effort_controllers/JointGroupEffortController
        
        joint1_position_controller:
          type: position_controllers/JointGroupPositionController
    
        joint1_velocity_controller:
          type: velocity_controllers/JointGroupVelocityController
    
        joint1_effort_controller:
          type: effort_controllers/JointGroupEffortController
        
        group_position_controller:
          type: position_controllers/JointGroupPositionController
    
        group_velocity_controller:
          type: velocity_controllers/JointGroupVelocityController
    
        group_effort_controller:
          type: effort_controllers/JointGroupEffortController
    
    joint0_position_controller:
      ros__parameters:
        joints:
          - joint0
    
    joint0_velocity_controller:
      ros__parameters:
        joints:
          - joint0
    
    joint0_effort_controller:
      ros__parameters:
        joints:
          - joint0
    
    joint1_position_controller:
      ros__parameters:
        joints:
          - joint1
    
    joint1_velocity_controller:
      ros__parameters:
        joints:
          - joint1
    
    joint1_effort_controller:
      ros__parameters:
        joints:
          - joint1
    
    group_position_controller:
      ros__parameters:
        joints:
          - joint0
          - joint1
    
    group_velocity_controller:
      ros__parameters:
        joints:
          - joint0
          - joint1
    
    group_effort_controller:
      ros__parameters:
        joints:
          - joint0
          - joint1
    

    This is my launch file

    # Copyright 2021 Factor Robotics
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    from launch import LaunchDescription
    from launch.actions import DeclareLaunchArgument
    from launch.conditions import IfCondition
    from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution
    from launch_ros.actions import Node
    from launch_ros.substitutions import FindPackageShare
    
    
    def generate_launch_description():
        declared_arguments = []
    
        declared_arguments.append(
            DeclareLaunchArgument(
                "enable_joint0",
                default_value="true",
            )
        )
    
        declared_arguments.append(
            DeclareLaunchArgument(
                "enable_joint1",
                default_value="true",
            )
        )
    
        declared_arguments.append(
            DeclareLaunchArgument(
                "joint0_controller",
                default_value="joint0_position_controller",
            )
        )
        declared_arguments.append(
            DeclareLaunchArgument(
                "joint1_controller",
                default_value="joint1_position_controller",
            )
        )
    
        enable_joint0 = LaunchConfiguration("enable_joint0")
        enable_joint1 = LaunchConfiguration("enable_joint1")
        joint0_controller = LaunchConfiguration("joint0_controller")
        joint1_controller = LaunchConfiguration("joint1_controller")
    
        robot_description_content = Command(
            [
                PathJoinSubstitution([FindExecutable(name="xacro")]),
                " ",
                PathJoinSubstitution(
                    [
                        FindPackageShare("odrive_description"),
                        "urdf",
                        "odrive.urdf.xacro",
                    ]
                ),
                " ",
                "enable_joint0:=",
                enable_joint0,
                " ",
                "enable_joint1:=",
                enable_joint1,
            ]
        )
        robot_description = {"robot_description": robot_description_content}
    
        robot_controllers = PathJoinSubstitution(
            [
                FindPackageShare("odrive_bringup"),
                "config",
                "odrive_controllers.yaml",
            ]
        )
    
        control_node = Node(
            package="controller_manager",
            executable="ros2_control_node",
            parameters=[robot_description, robot_controllers],
            output={
                "stdout": "screen",
                "stderr": "screen",
            },
        )
    
        robot_state_pub_node = Node(
            package="robot_state_publisher",
            executable="robot_state_publisher",
            output="both",
            parameters=[robot_description],
        )
    
        joint_state_broadcaster_spawner = Node(
            package="controller_manager",
            executable="spawner.py",
            arguments=["joint_state_broadcaster", "--controller-manager", "/controller_manager"],
        )
    
        joint0_controller_spawner = Node(
            package="controller_manager",
            executable="spawner.py",
            arguments=[joint0_controller, "-c", "/controller_manager"],
            condition=IfCondition(enable_joint0),
        )
    
        joint1_controller_spawner = Node(
            package="controller_manager",
            executable="spawner.py",
            arguments=[joint1_controller, "-c", "/controller_manager"],
            condition=IfCondition(enable_joint1),
        )
    
        nodes = [
            control_node,
            robot_state_pub_node,
            joint_state_broadcaster_spawner,
            joint0_controller_spawner,
            joint1_controller_spawner,
        ]
    
        return LaunchDescription(declared_arguments + nodes)
    

    [ERROR] [ros2_control_node-1]: process has died [pid 28410, exit code -6, cmd '/opt/ros/foxy/lib/controller_manager/ros2_control_node --ros-args --params-file /tmp/launch_params_0nfwf8mm --params-file /home/khadas/workspace/install/odrive_bringup/share/odrive_bringup/config/odrive_controllers.yaml']. Could any one tell me the reason why it fails?

    opened by henryliuliuliu 5
  • Is it possible to read a bus voltage from this package?

    Is it possible to read a bus voltage from this package?

    I see that in the documentation it says that it is possible to read the voltage sensor, but I have not been able to find information on how to do it in the documentation and if it is possible to put it in some topic to ros, does anyone know if it is possible to do it and if they have an example or guide to do it? Thanks

    opened by Gilocho 1
  • Unit of the position data

    Unit of the position data

    Hello,

    My motor's CPR (count per revolution) is set to 4000. However, when I send position command 4000, it rotates more than 7 revolutions. What is the unit of the data I am sending through the topic, joint0_position_controller/commands?

    Thank you

    opened by likemountain 4
  • RTPS_TRANSPORT_SHM ERROR

    RTPS_TRANSPORT_SHM ERROR

    Hello,

    I was able to launch the odrive_bringup, but I see this error message when I publish the velocity command.

    It's kind of weird because my motor is moving when publishing a really high number as my velocity, but the velocity is not constant. It's kind of jerking. It repeats rotating fast and slow. I wonder if this is because of the error message or because my control gains are wrong.

    Does anybody know how to resolve this issue?

    Screenshot at 2022-03-19 10-20-32

    opened by likemountain 3
Owner
Nanjing Factor Robotics Co., Ltd.
null
Loads a signed kernel driver which allows you to map any driver to kernel mode without any traces of the signed / mapped driver.

CosMapper Loads a signed kernel driver (signed with leaked cert) which allows you to map any driver to kernel mode without any traces of the signed /

null 157 Jan 2, 2023
x64 Windows kernel driver mapper, inject unsigned driver using anycall

anymapper x64 Windows kernel driver mapper, inject unsigned driver using anycall This project is WIP. Todo Fix: Can't make API calls from IAT nor func

Kento Oki 72 Dec 26, 2022
Driver leap - Self-sustainable fork of SteamVR driver for Leap Motion controller with updated vendor libraries

Driver Leap Self-sustainable fork of SteamVR driver for Leap Motion controller with updated vendor libraries Installation (for users) Install Ultralea

null 68 Jan 5, 2023
Hygieia, a vulnerable driver traces scanner written in C++ as an x64 Windows kernel driver.

Hygieia The Greek goddess of health, her name is the source for the word "hygiene". Hygieia is a windows driver that works similarly to how pagewalkr

Deputation 103 Dec 4, 2022
SinMapper - usermode driver mapper that forcefully loads any signed kernel driver

usermode driver mapper that forcefully loads any signed kernel driver (legit cert) with a big enough section (example: .data, .rdata) to map your driver over. the main focus of this project is to prevent modern anti-cheats (BattlEye, EAC) from finding your driver and having the power to hook anything due to being inside of legit memory (signed legit driver).

null 170 Dec 29, 2022
Lotus 1-2-3 R4D Display Driver for DOSEMU

Lotus 1-2-3 R4D Display Driver for DOSEMU2 This is a WIP display driver for Lotus 1-2-3 R4D to enable support for arbitrary text resolutions in DOSEMU

Tavis Ormandy 112 Dec 2, 2022
Simple Player-Glow & Driver Source Included

External-Apex-Cheat Install WDK Build in Release x64 Map driver using KDMapper Launch Game Run User-mode when in main menu Have Fun driver is indeed p

null 103 Dec 21, 2022
MPU-9250 sensor driver.

mpu9250 This library communicates with InvenSense MPU-9250 and MPU-9255 Inertial Measurement Units (IMUs). License Changelog Contributing guide Descri

Bolder Flight Systems 413 Dec 30, 2022
An OpenVR Driver for VR Gloves

lucidgloves - LucidVR X Fngrs Developed by: Danwillm Lucas_VRTech What is this? This repository contains the OpenVR(SteamVR) driver for a DIY VR Hapti

null 396 Jan 6, 2023
Corsair LL Access driver abuse

CorsairLLeak Map physical addresses into userspace (RW), read/write MSRs, send/recieve data on I/O ports, and query/set bus configuration data with th

Arush Agarampur 21 Jun 27, 2022
Valorant cheat that abuses Logitech GHUB driver input management.

valorant-cheat Valorant cheat that uses Logitech GHUB driver input management and color detection. features Triggerbot with customizable delay (VK_SHI

null 30 Jan 7, 2023
ASUSTeK AsIO3 I/O driver unlock

AsIo3Unlock ASUSTeK AsIO3 I/O driver unlock Purpose This is proof-of-concept bypass of pseudo-security caller check implemented in AsIO3, "unlocking"

null 20 Nov 8, 2022
A FAT filesystem with SPI driver for SD card on Raspberry Pi Pico

no-OS-FatFS-SD-SPI-RPi-Pico Simple library for SD Cards on the Pico At the heart of this library is ChaN's FatFs - Generic FAT Filesystem Module. It a

Carl J Kugler III 130 Dec 27, 2022
OffensivePH - use old Process Hacker driver to bypass several user-mode access controls

offensiveph OffensivePH is a post-exploitation tool that utilizes an old Process Hacker driver to bypass several user-mode access controls. Usage Comp

Red Section 291 Dec 29, 2022
ads1115 full function driver

LibDriver ADS1115 English | 简体中文 ADS1115 is an ultra small package, low power, IIC bus interface, 16 bit conversion accuracy, internal voltage referen

Shifeng Li 40 Dec 23, 2022
Linux USB driver for the MOTU AVB series interfaces

motu-avb Linux USB driver for the MOTU AVB series interfaces Kernel parameters: samplerate: set the samplerate (its currently fixed at module load) de

null 47 Dec 23, 2022
A kernel level driver for Windows built to configure the Blue Screen Of Death

BSODConfigure A kernel level driver for Windows built to configure the Blue Screen Of Death. Go see the writeup at https://www.phasetw0.com/configurin

phasetw0 14 Dec 22, 2022
Driver for the TI TMP102 I2C temperature sensor. For use with TI TM4C123X MCUs.

TI TMP102 Driver Simple driver for the TI TMP102 I2C temperature sensor. For use with TI TM4C123X MCUs and TI's TivaWare™ Peripheral Driver Library. D

Dan Maher 4 Jun 27, 2021
bmp180 full function driver

LibDriver BMP180 English | 简体中文 The BMP180 is the function compatible successor of the BMP085, a new generation of high precision digital pressure sen

Shifeng Li 58 Dec 23, 2022