Added working CAN and first draft of sim with chrono

This commit is contained in:
wittenator 2023-12-26 18:17:41 +01:00
parent c307af491e
commit e5dd1bb3ff
21 changed files with 488 additions and 911 deletions

1
src/chabo_chrono Submodule

@ -0,0 +1 @@
Subproject commit 3064103811a228c1ad5e50f1b75006c04a36d6b9

View File

@ -17,9 +17,43 @@ endif()
# find dependencies # find dependencies
find_package(ament_cmake REQUIRED) find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in find_package(ignition-cmake2 REQUIRED)
# further dependencies manually. find_package(ignition-msgs5 REQUIRED)
# find_package(<dependency> REQUIRED) find_package(ignition-transport8 REQUIRED)
find_package(ignition-math6 REQUIRED)
find_package(ignition-common3 REQUIRED)
find_package(ignition-plugin1 REQUIRED COMPONENTS register)
set(IGN_PLUGIN_VER ${ignition-plugin1_VERSION_MAJOR})
find_package(ignition-gazebo6 REQUIRED)
# Add sources for each plugin to be registered.
add_library(SetLinkStatePlugin src/SetLinkStatePlugin.cpp)
target_include_directories(
SetLinkStatePlugin
PRIVATE
include
)
set_property(TARGET SetLinkStatePlugin PROPERTY CXX_STANDARD 17)
target_link_libraries(SetLinkStatePlugin
PRIVATE ignition-plugin${IGN_PLUGIN_VER}::ignition-plugin${IGN_PLUGIN_VER}
PRIVATE ignition-gazebo6::ignition-gazebo6)
add_executable(SetPos src/external_pose_set.cpp)
target_include_directories(
SetPos
PRIVATE
include
)
target_link_libraries(SetPos ${IGNITION-MSGS_LIBRARIES} ${IGNITION-TRANSPORT_LIBRARIES} ${IGNITION-COMMON_LIBRARIES} ${IGNITION-MATH_LIBRARIES})
install(TARGETS SetPos DESTINATION lib/${PROJECT_NAME})
if(BUILD_TESTING) if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED) find_package(ament_lint_auto REQUIRED)
@ -33,7 +67,7 @@ if(BUILD_TESTING)
endif() endif()
install( install(
DIRECTORY config description launch worlds src DIRECTORY config description launch worlds ft24_control
DESTINATION share/${PROJECT_NAME} DESTINATION share/${PROJECT_NAME}
) )
@ -41,9 +75,9 @@ ament_python_install_package(${PROJECT_NAME})
# Install Python executables # Install Python executables
install(PROGRAMS install(PROGRAMS
src/imu_covariance_adder.py ft24_control/imu_covariance_adder.py
src/joy.py ft24_control/joy.py
src/twist.py ft24_control/twist.py
DESTINATION lib/${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME}
) )

View File

@ -21,7 +21,7 @@ ackermann_steering_controller:
front_steering: true front_steering: true
open_loop: false open_loop: false
velocity_rolling_window_size: 10 velocity_rolling_window_size: 10
position_feedback: true position_feedback: false
use_stamped_vel: false use_stamped_vel: false
rear_wheels_names: [rear_right_wheel_joint, rear_left_wheel_joint] rear_wheels_names: [rear_right_wheel_joint, rear_left_wheel_joint]
front_wheels_names: [front_right_steer_joint, front_left_steer_joint] front_wheels_names: [front_right_steer_joint, front_left_steer_joint]

View File

@ -13,23 +13,19 @@
<!-- Note everything below here is the same as the Gazebo one --> <!-- Note everything below here is the same as the Gazebo one -->
<joint name="rear_right_wheel_joint"> <joint name="rear_right_wheel_joint">
<command_interface name="velocity"/> <command_interface name="velocity"/>
<state_interface name="position"/>
<state_interface name="velocity"/> <state_interface name="velocity"/>
</joint> </joint>
<joint name="rear_left_wheel_joint"> <joint name="rear_left_wheel_joint">
<command_interface name="velocity"/> <command_interface name="velocity"/>
<state_interface name="position"/>
<state_interface name="velocity"/> <state_interface name="velocity"/>
</joint> </joint>
<joint name="front_left_steer_joint"> <joint name="front_left_steer_joint">
<command_interface name="position"/> <command_interface name="position"/>
<state_interface name="position"/> <state_interface name="position"/>
<state_interface name="velocity"/>
</joint> </joint>
<joint name="front_right_steer_joint"> <joint name="front_right_steer_joint">
<command_interface name="position"/> <command_interface name="position"/>
<state_interface name="position"/> <state_interface name="position"/>
<state_interface name="velocity"/>
</joint> </joint>
</ros2_control> </ros2_control>
<gazebo> <gazebo>

View File

@ -6,23 +6,19 @@
</hardware> </hardware>
<joint name="rear_right_wheel_joint"> <joint name="rear_right_wheel_joint">
<command_interface name="velocity"/> <command_interface name="velocity"/>
<state_interface name="position"/>
<state_interface name="velocity"/> <state_interface name="velocity"/>
</joint> </joint>
<joint name="rear_left_wheel_joint"> <joint name="rear_left_wheel_joint">
<command_interface name="velocity"/> <command_interface name="velocity"/>
<state_interface name="position"/>
<state_interface name="velocity"/> <state_interface name="velocity"/>
</joint> </joint>
<joint name="front_left_steer_joint"> <joint name="front_left_steer_joint">
<command_interface name="position"/> <command_interface name="position"/>
<state_interface name="position"/> <state_interface name="position"/>
<state_interface name="velocity"/>
</joint> </joint>
<joint name="front_right_steer_joint"> <joint name="front_right_steer_joint">
<command_interface name="position"/> <command_interface name="position"/>
<state_interface name="position"/> <state_interface name="position"/>
<state_interface name="velocity"/>
</joint> </joint>
</ros2_control> </ros2_control>
<gazebo> <gazebo>

View File

@ -322,6 +322,14 @@
<gazebo reference="${name}_wheel_link"> <gazebo reference="${name}_wheel_link">
<material>Gazebo/Red</material> <material>Gazebo/Red</material>
<collision>
<friction>
<ode>
<mu>0</mu>
<mu2>0</mu2>
</ode>
</friction>
</collision>
</gazebo> </gazebo>
</xacro:macro> </xacro:macro>
</robot> </robot>

View File

@ -0,0 +1,84 @@
#!/usr/bin/env python3
import math
import time
# https://github.com/srmainwaring/python-ignition
from ignition.msgs.header_pb2 import Header
from ignition.msgs.pose_pb2 import Pose
from ignition.msgs.quaternion_pb2 import Quaternion
from ignition.msgs.vector3d_pb2 import Vector3d
from ignition.transport import Node
# https://github.com/srmainwaring/python-ignition
import ignition.math
# Settings
world_name = "empty"
model_name = "iris"
timeout_ms = 10
update_rate_hz = 30.0
# offsets - The ArduPilot z position may be zeroed to the ground plane,
# Gazebo will set pose for the base link geometric centre
pose_offset_x = 0.0
pose_offset_y = 0.0
pose_offset_z = 0.17
def main():
connection_string = "udp:127.0.0.1:14550"
# Connect to the Vehicle.
print("Connecting to vehicle on: {}".format(connection_string))
try:
# create a transport node
node = Node()
# set service details
service = "/world/{}/set_pose".format(world_name)
reqtype = "ignition.msgs.Pose"
reptype = "ignition.msgs.Boolean"
# configure update loop
now_s = time.time()
start_time_s = now_s
# update_rate_hz = 1.0
update_period_s = 1.0 / update_rate_hz
last_update_time_s = now_s
sim_time_s = now_s - start_time_s
while True:
now_s = time.time()
time_s = now_s - start_time_s
if now_s - last_update_time_s >= update_period_s:
last_update_time_s = now_s
# check we have valid data
# create request message
vector3d_msg = Vector3d()
vector3d_msg.x = 0.01 + pose_offset_x
vector3d_msg.y = 0.01+ pose_offset_y
vector3d_msg.z = pose_offset_z
quat_msg = Quaternion()
pose_msg = Pose()
pose_msg.name = model_name
pose_msg.position.CopyFrom(vector3d_msg)
pose_msg.orientation.CopyFrom(quat_msg)
# submit request (blocking)
result = node.request(service, pose_msg, timeout_ms, reptype)
if not result:
print("[{:.1f}] update failed".format(time_s))
except KeyboardInterrupt:
pass

View File

@ -0,0 +1,108 @@
import numpy as np
import cv2
import matplotlib.pyplot as plt
def generate_straight(length, width, start, direction=(1, 0)):
direction = np.array(direction)
norm_dir = direction / np.linalg.norm(direction)
left_edge = [start + norm_dir * width / 2 * np.array([-1, 1]) * i for i in range(length)]
right_edge = [start + norm_dir * width / 2 * np.array([1, -1]) * i for i in range(length)]
return left_edge, right_edge
def generate_hairpin(radius, width, center, entry_angle=0):
angles = np.linspace(entry_angle, entry_angle + np.pi, 180)
left_edge = [center + (radius + width / 2) * np.array([np.cos(a), np.sin(a)]) for a in angles]
right_edge = [center + (radius - width / 2) * np.array([np.cos(a), np.sin(a)]) for a in angles]
return left_edge, right_edge
def generate_chicane(segment_lengths, width, start, direction=(1, 0)):
direction = np.array(direction)
norm_dir = direction / np.linalg.norm(direction)
orth_dir = np.array([-norm_dir[1], norm_dir[0]])
points = [start]
for i, length in enumerate(segment_lengths):
start += norm_dir * length if i % 2 == 0 else orth_dir * width * ((i % 4) - 2)
points.append(start)
left_edge = [p + orth_dir * width / 2 for p in points]
right_edge = [p - orth_dir * width / 2 for p in points]
return left_edge, right_edge
def generate_track():
# Define the track dimensions and segments
straight_length = 100
hairpin_radius = 20
chicane_segment_lengths = [20, 10, 20]
track_width = 5
# Generate track segments
straight_start = np.array([0, 0])
straight_end = np.array([straight_length, 0])
left_straight, right_straight = generate_straight(straight_length, track_width, straight_start)
hairpin_center = straight_end + np.array([0, -hairpin_radius])
left_hairpin, right_hairpin = generate_hairpin(hairpin_radius, track_width, hairpin_center)
chicane_start = left_hairpin[-1]
left_chicane, right_chicane = generate_chicane(chicane_segment_lengths, track_width, chicane_start)
# Combine track segments
left_track = left_straight + left_hairpin[::-1] + left_chicane
right_track = right_straight + right_hairpin[::-1] + right_chicane
# move center to (100, 100)
left_track = left_track + np.array([100, 100])
right_track = right_track + np.array([100, 100])
return left_track, right_track
def convert_to_grid_coordinates(cone, map_size_meters, map_resolution):
x, y = cone
grid_x = int(x / map_resolution)
grid_y = int(y / map_resolution)
return grid_x, grid_y
def create_occupancy_grid(left_cones, right_cones, map_size_meters, map_resolution):
map_size_cells = int(map_size_meters / map_resolution)
grid_image = np.full((map_size_cells, map_size_cells), -1, dtype=np.int8)
max_weight = 100
min_weight = 50
weight_decrement = 5
cones = []
for cone in np.concatenate([left_cones, right_cones], axis=0):
left_x, left_y = convert_to_grid_coordinates(cone, map_size_meters, map_resolution)
cones.append((left_x, left_y))
cones = np.array([cones], dtype=np.int32)
cv2.fillPoly(grid_image, cones, color=0)
return grid_image
def plot_occupancy_grid(grid, map_size_meters, map_resolution):
plt.imshow(grid, cmap='gray', origin='lower')
plt.title('Occupancy Grid ({}m x {}m, {}m/pixel)'.format(map_size_meters, map_size_meters, map_resolution))
plt.xlabel('X Position (m)')
plt.ylabel('Y Position (m)')
plt.xticks(np.arange(0, map_size_meters/map_resolution, step=10), np.arange(0, map_size_meters, step=10*map_resolution))
plt.yticks(np.arange(0, map_size_meters/map_resolution, step=10), np.arange(0, map_size_meters, step=10*map_resolution))
plt.show()
# Test Parameters
map_size_meters = 200
map_resolution = 0.1 # 1 meter per pixel for simplicity
# Generate and plot test track
left_cones, right_cones = generate_track()
grid = create_occupancy_grid(left_cones, right_cones, map_size_meters, map_resolution)
plot_occupancy_grid(grid, map_size_meters, map_resolution)

View File

@ -0,0 +1,34 @@
#ifndef SET_LINK_STATE_SYSTEM_PLUGIN_H
#define SET_LINK_STATE_SYSTEM_PLUGIN_H
#include <gz/sim/System.hh>
#include <gz/sim/EventManager.hh>
using namespace gz::sim;
namespace set_link_state_system_plugin
{
class SetLinkStateSystemPlugin:
public gz::sim::System,
public gz::sim::ISystemConfigure,
public gz::sim::ISystemUpdate
{
public:
/// \brief Constructor
SetLinkStateSystemPlugin() = default;
/// \brief Destructor
virtual ~SetLinkStateSystemPlugin() = default;
// Ignition Gazebo System interface
public: void Configure(const Entity & _entity, const std::shared_ptr<const sdf::Element> & _sdf,
gz::sim::EntityComponentManager & _ecm, gz::sim::EventManager & _eventManager) override;
public: void Update(const gz::sim::UpdateInfo &_info,
gz::sim::EntityComponentManager &_ecm) override;
};
}
#endif // SET_LINK_STATE_SYSTEM_PLUGIN_H

View File

@ -0,0 +1,47 @@
#include "../include/SetLinkStatePlugin.hpp"
//! [registerSampleSystem]
#include <gz/plugin/RegisterMore.hh>
#include <gz/sim/components.hh>
using namespace components;
// Include a line in your source file for each interface implemented.
IGNITION_ADD_PLUGIN(
set_link_state_system_plugin::SetLinkStateSystemPlugin,
gz::sim::System,
set_link_state_system_plugin::SetLinkStateSystemPlugin::ISystemConfigure,
set_link_state_system_plugin::SetLinkStateSystemPlugin::ISystemUpdate
)
//! [registerSampleSystem]
//! [implementSampleSystem]
using namespace set_link_state_system_plugin;
using namespace ignition::gazebo;
// Ignition Gazebo System interface
void SetLinkStateSystemPlugin::Configure(const Entity & _entity, const std::shared_ptr<const sdf::Element> & _sdf,
EntityComponentManager & _ecm, EventManager & _eventManager)
{
// Get the world entity
auto worldEntity = _ecm.EntityByComponents(components::Name("world"), components::World());
if (worldEntity == kNullEntity)
{
ignerr << "Failed to find the world entity.\n";
return;
}
ignmsg << "SetLinkStateSystemPlugin configured.\n";
}
void SetLinkStateSystemPlugin::Update(const UpdateInfo & _info, EntityComponentManager & _ecm)
{
// Get the current simulation time in Fortress
// TODO: Implement communication with Fortress co-simulation script to get link states, velocities, accelerations, etc.
//
}

View File

@ -0,0 +1,84 @@
#include <ignition/msgs.hh>
#include <ignition/transport/Node.hh>
#include <iostream>
#include <cmath>
#include <chrono>
#include <thread>
// Settings
const std::string world_name = "empty";
const std::string model_name = "spawn_robot";
const int timeout_ms = 10;
const double update_rate_hz = 100.0;
// Offsets
double pose_offset_x = 0.0;
double pose_offset_y = 0.0;
double pose_offset_z = 0.17;
int main()
{
// Connect to the Ignition Transport node
ignition::transport::Node node;
// Set service details
const std::string service = "/world/" + world_name + "/set_pose";
const std::string reqtype = "ignition.msgs.Pose";
const std::string reptype = "ignition.msgs.Boolean";
// Configure the update loop
auto start_time = std::chrono::steady_clock::now();
double update_period_s = 1.0 / update_rate_hz;
auto last_update_time = start_time;
while (true)
{
auto now = std::chrono::steady_clock::now();
auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
double time_s = time_elapsed.count() / 1000.0;
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update_time).count() >= update_period_s * 1000.0)
{
last_update_time = now;
// Create request message
ignition::msgs::Vector3d vector3d_msg;
pose_offset_x = 10 * sin(time_s);
pose_offset_y = 10 * cos(time_s);
pose_offset_z = 0.17;
vector3d_msg.set_x(pose_offset_x);
vector3d_msg.set_y(pose_offset_y);
vector3d_msg.set_z(pose_offset_z);
ignition::msgs::Quaternion quat_msg;
// follow circle
double yaw = atan2(pose_offset_y, pose_offset_x);
quat_msg.set_w(cos(yaw / 2));
quat_msg.set_x(0);
quat_msg.set_y(0);
quat_msg.set_z(sin(yaw / 2));
ignition::msgs::Pose pose_msg;
pose_msg.set_name(model_name);
pose_msg.mutable_position()->CopyFrom(vector3d_msg);
pose_msg.mutable_orientation()->CopyFrom(quat_msg);
bool result;
ignition::msgs::Boolean result_msg;
// Submit request (blocking)
result = node.Request(service, pose_msg, timeout_ms, result_msg, result);
if (!result)
{
std::cout << "[" << time_s << "] Update failed" << std::endl;
}
}
// Sleep for a short duration to avoid high CPU usage
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}

View File

@ -1,74 +0,0 @@
import cv2
import numpy as np
def draw_filled_track_boundaries(image, left_boundary, right_boundary):
# Combine left and right boundaries to create a single polygon
track_polygon = np.concatenate((left_boundary, right_boundary[::-1]))
# Draw filled area between left and right boundaries
cv2.fillPoly(image, [track_polygon], color=(255, 255, 255))
def transform_boundaries(left_boundary, right_boundary, car_position, car_orientation):
# Homogeneous transformation matrix
transform_matrix = np.array([[np.cos(car_orientation), -np.sin(car_orientation), car_position[0]],
[np.sin(car_orientation), np.cos(car_orientation), car_position[1]],
[0, 0, 1]])
# Add homogeneous coordinate to boundaries
left_boundary_homogeneous = np.column_stack((left_boundary, np.ones(len(left_boundary))))
right_boundary_homogeneous = np.column_stack((right_boundary, np.ones(len(right_boundary))))
# Apply transformation
left_boundary_transformed = np.dot(transform_matrix, left_boundary_homogeneous.T).T[:, :2].astype(np.int32)
right_boundary_transformed = np.dot(transform_matrix, right_boundary_homogeneous.T).T[:, :2].astype(np.int32)
return left_boundary_transformed, right_boundary_transformed
def main():
# Image size and background color
image_size = (800, 600)
background_color = (0, 0, 0) # Black background
# Create a black image
map_image = np.zeros((image_size[1], image_size[0], 3), dtype=np.uint8)
map_image[:] = background_color
# Example curved track boundaries (replace with your own points)
num_points = 100
theta = np.linspace(0, 2 * np.pi, num_points)
radius = 150
left_boundary_x = 400 + radius * np.cos(theta)
left_boundary_y = 300 + radius * np.sin(theta)
right_boundary_x = 400 + radius * np.cos(theta - np.pi)
right_boundary_y = 500 + radius * np.sin(theta - np.pi)
left_boundary = np.column_stack((left_boundary_x, left_boundary_y)).astype(np.int32)
right_boundary = np.column_stack((right_boundary_x, right_boundary_y)).astype(np.int32)
# Car's global position and orientation (replace with your own values)
car_position = (100, 100)
car_orientation = np.pi / 4 # 45 degrees
# Transform boundaries based on car's global position and orientation
left_boundary_transformed, right_boundary_transformed = transform_boundaries(
left_boundary, right_boundary, car_position, car_orientation
)
# Draw filled area between track boundaries on the image
draw_filled_track_boundaries(map_image, left_boundary_transformed, right_boundary_transformed)
# Display the map image
cv2.imshow('Transformed Track Map', map_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == "__main__":
main()

View File

@ -1,43 +1,23 @@
<?xml version="1.0" ?> <?xml version="1.0" ?>
<sdf version="1.6"> <sdf version="1.6">
<world name="empty"> <world name="empty" >
<physics name="phys" type="dart"> <physics name="phys" type="dart">
<max_step_size>0.005</max_step_size> <max_step_size>0.005</max_step_size>
<real_time_factor>1.0</real_time_factor>
<ode>
<solver>
<type>quick</type>
<island_threads>true</island_threads>
<thread_position_correction>true</thread_position_correction>
<friction_model>cone_model</friction_model>
</solver>
</ode>
<dart>
<collision_detector>
bullet
</collision_detector>
</dart>
</physics> </physics>
<plugin <plugin
filename="gz-sim-physics-system" filename="ignition-gazebo-physics-system"
name="gz::sim::systems::Physics"> name="ignition::gazebo::systems::Physics">
</plugin> </plugin>
<plugin <plugin
filename="gz-sim-user-commands-system" filename="ignition-gazebo-user-commands-system"
name="gz::sim::systems::UserCommands"> name="ignition::gazebo::systems::UserCommands">
</plugin> </plugin>
<plugin <plugin
filename="gz-sim-scene-broadcaster-system" filename="ignition-gazebo-scene-broadcaster-system"
name="gz::sim::systems::SceneBroadcaster"> name="ignition::gazebo::systems::SceneBroadcaster">
</plugin> </plugin>
<plugin <plugin filename="ignition-gazebo-sensors-system"
filename="gz-sim-contact-system" name="ignition::gazebo::systems::Sensors">
name="gz::sim::systems::Contact">
</plugin>
<plugin filename="gz-sim-imu-system.so"
name="gz::sim::systems::Imu">
</plugin>
<plugin filename="gz-sim-sensors-system.so" name="gz::sim::systems::Sensors">
<render_engine>ogre2</render_engine> <render_engine>ogre2</render_engine>
</plugin> </plugin>
<scene> <scene>

View File

@ -185,19 +185,7 @@ if __name__ == "__main__":
255 255
</laser_retro> </laser_retro>
</visual> </visual>
<sensor name='sensor_contact' type='contact'>
<contact>
<collision>collision</collision>
</contact>
</sensor>
</link> </link>
<plugin filename="libignition-gazebo-touchplugin-system.so"
name="ignition::gazebo::systems::TouchPlugin">
<target>vehicle_blue</target>
<namespace>wall</namespace>
<time>0.001</time>
<enabled>true</enabled>
</plugin>
</model> </model>
''' '''
cone_list_sdf.append(cone_sdf) cone_list_sdf.append(cone_sdf)

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,14 @@
#include "wheel.h" #include "wheel.h"
#include "can1__main_ft24.h" #include "can1__main_ft24.h"
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
using hardware_interface::CallbackReturn; using hardware_interface::CallbackReturn;
using hardware_interface::return_type; using hardware_interface::return_type;
@ -52,7 +60,8 @@ private:
can1__main_ft24_jetson_tx_t tx_frame; can1__main_ft24_jetson_tx_t tx_frame;
can1__main_ft24_jetson_rx_t rx_frame; can1__main_ft24_jetson_rx_t rx_frame;
uint8_t can_buffer[64]; uint8_t* can_buffer;
struct can_frame frame;
rclcpp::Logger logger_; rclcpp::Logger logger_;

View File

@ -48,12 +48,41 @@ CallbackReturn Ros2ControlCAN::on_init(const hardware_interface::HardwareInfo &
// Set up the wheels // Set up the wheels
f_l_wheel_.setup(cfg_.front_left_wheel_name); f_l_wheel_.setup(cfg_.front_left_wheel_name);
f_r_wheel_.setup(cfg_.front_right_wheel_name); f_r_wheel_.setup(cfg_.front_right_wheel_name);
r_l_wheel_.setup(cfg_.front_left_wheel_name); r_l_wheel_.setup(cfg_.rear_left_wheel_name);
r_r_wheel_.setup(cfg_.front_right_wheel_name); r_r_wheel_.setup(cfg_.rear_right_wheel_name);
// init tx frame // init tx frame
can1__main_ft24_jetson_tx_init(&tx_frame); can1__main_ft24_jetson_tx_init(&tx_frame);
if ((socket_instance = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Socket");
return CallbackReturn::ERROR;
}
struct ifreq ifr;
const char *cstr = cfg_.device.c_str();
strcpy(ifr.ifr_name, cstr );
ioctl(socket_instance, SIOCGIFINDEX, &ifr);
struct sockaddr_can addr;
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(socket_instance, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Bind");
return CallbackReturn::ERROR;
}
//struct can_filter rfilter[1];
//rfilter[0].can_id = 0x550;
//rfilter[0].can_mask = 0xFF0;
//rfilter[1].can_id = 0x200;
//rfilter[1].can_mask = 0x700;
//setsockopt(socket_instance, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
// allocate can_buffer to 64 bit
can_buffer = (uint8_t*) malloc(8);
RCLCPP_INFO(logger_, "Finished Configuration"); RCLCPP_INFO(logger_, "Finished Configuration");
return CallbackReturn::SUCCESS; return CallbackReturn::SUCCESS;
@ -69,8 +98,6 @@ std::vector<hardware_interface::StateInterface> Ros2ControlCAN::export_state_int
state_interfaces.emplace_back(hardware_interface::StateInterface(f_r_wheel_.name, hardware_interface::HW_IF_POSITION, &f_r_wheel_.steer)); state_interfaces.emplace_back(hardware_interface::StateInterface(f_r_wheel_.name, hardware_interface::HW_IF_POSITION, &f_r_wheel_.steer));
state_interfaces.emplace_back(hardware_interface::StateInterface(r_l_wheel_.name, hardware_interface::HW_IF_VELOCITY, &r_l_wheel_.vel)); state_interfaces.emplace_back(hardware_interface::StateInterface(r_l_wheel_.name, hardware_interface::HW_IF_VELOCITY, &r_l_wheel_.vel));
state_interfaces.emplace_back(hardware_interface::StateInterface(r_r_wheel_.name, hardware_interface::HW_IF_VELOCITY, &r_r_wheel_.vel)); state_interfaces.emplace_back(hardware_interface::StateInterface(r_r_wheel_.name, hardware_interface::HW_IF_VELOCITY, &r_r_wheel_.vel));
state_interfaces.emplace_back(hardware_interface::StateInterface(r_l_wheel_.name, hardware_interface::HW_IF_POSITION, &r_l_wheel_.pos));
state_interfaces.emplace_back(hardware_interface::StateInterface(r_r_wheel_.name, hardware_interface::HW_IF_POSITION, &r_r_wheel_.pos));
return state_interfaces; return state_interfaces;
} }
@ -93,34 +120,8 @@ CallbackReturn Ros2ControlCAN::on_activate(const rclcpp_lifecycle::State & previ
{ {
RCLCPP_INFO(logger_, "Starting Controller..."); RCLCPP_INFO(logger_, "Starting Controller...");
if ((socket_instance = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Socket");
return CallbackReturn::ERROR;
}
struct ifreq ifr;
const char *cstr = cfg_.device.c_str();
strcpy(ifr.ifr_name, cstr );
ioctl(socket_instance, SIOCGIFINDEX, &ifr);
struct sockaddr_can addr;
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(socket_instance, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Bind");
return CallbackReturn::ERROR;
}
struct can_filter rfilter[1];
rfilter[0].can_id = 0x550;
rfilter[0].can_mask = 0xFF0;
//rfilter[1].can_id = 0x200;
//rfilter[1].can_mask = 0x700;
setsockopt(socket_instance, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
return CallbackReturn::SUCCESS; return CallbackReturn::SUCCESS;
} }
CallbackReturn Ros2ControlCAN::on_deactivate(const rclcpp_lifecycle::State & previous_state) CallbackReturn Ros2ControlCAN::on_deactivate(const rclcpp_lifecycle::State & previous_state)
{ {
@ -145,45 +146,42 @@ return_type Ros2ControlCAN::read(const rclcpp::Time & /* time */, const rclcpp::
double deltaSeconds = diff.count(); double deltaSeconds = diff.count();
time_ = new_time; time_ = new_time;
// read can frame
if (false) if (::read(socket_instance, &frame, sizeof(struct can_frame)) < 0) {
{ perror("Read from device: ");
return return_type::ERROR; return return_type::ERROR;
} }
// convert rpm to rad/s // decode can frame
//l_wheel_.vel = RPMSTORADS(l_wheel_.vel); int rx_size = can1__main_ft24_jetson_rx_unpack(&rx_frame, reinterpret_cast<uint8_t*>(&frame.data), sizeof(frame.data));
//r_wheel_.vel = RPMSTORADS(r_wheel_.vel);
if (rx_size < 0) {
perror("Unpack");
return return_type::ERROR;
}
return return_type::OK; return return_type::OK;
} }
return_type Ros2ControlCAN::write(const rclcpp::Time & /* time */, const rclcpp::Duration & /* period */) return_type Ros2ControlCAN::write(const rclcpp::Time&, const rclcpp::Duration&)
{ {
if (false) tx_frame.jetson_steering_angle = can1__main_ft24_jetson_tx_jetson_steering_angle_encode((f_l_wheel_.steer + f_r_wheel_.steer) / 2.0);
{ tx_frame.jetson_speed_target = can1__main_ft24_jetson_tx_jetson_speed_target_encode((r_l_wheel_.vel + r_r_wheel_.vel) / 2.0);
return return_type::ERROR;
}
// send can frame int tx_size = can1__main_ft24_jetson_tx_pack(reinterpret_cast<uint8_t*>(&frame.data), &tx_frame, sizeof(frame.data));
tx_frame.jetson_steering_angle = can1__main_ft24_jetson_tx_jetson_steering_angle_encode((f_l_wheel_.steer + f_r_wheel_.steer)/2.0);
// pack tx_frame
int tx_size = can1__main_ft24_jetson_tx_pack(can_buffer, &tx_frame, 64);
if (tx_size < 0) { if (tx_size < 0) {
perror("Pack"); perror("Pack");
return return_type::ERROR; return return_type::ERROR;
} }
// send tx_frame frame.can_id = 0xe1;
struct can_frame frame;
frame.can_id = 0x550;
frame.can_dlc = tx_size; frame.can_dlc = tx_size;
memcpy(frame.data, can_buffer, tx_size);
if (::write(socket_instance, &frame, sizeof(struct can_frame)) < 0) { if (::write(socket_instance, &frame, sizeof(struct can_frame)) < 0) {
perror("Write"); perror("Write to device: ");
// print error with socket instance info
return return_type::ERROR; return return_type::ERROR;
} }