merging master
This commit is contained in:
commit
09f7e5da8a
@ -13,18 +13,23 @@ diff_cont:
|
||||
|
||||
publish_rate: 30.0 # You can set this higher than the controller manager update rate, but it will be throttled
|
||||
base_frame_id: base_link
|
||||
publish_limited_velocity: true
|
||||
|
||||
left_wheel_names: ['left_wheel_joint']
|
||||
right_wheel_names: ['right_wheel_joint']
|
||||
wheel_separation: 0.215
|
||||
wheel_radius: 0.049338032
|
||||
|
||||
# linear:
|
||||
# x:
|
||||
# max_acceleration: 0.15
|
||||
# has_acceleration_limits: true
|
||||
|
||||
use_stamped_vel: false
|
||||
|
||||
# joint_broad:
|
||||
# ros__parameters:
|
||||
joint_limits:
|
||||
ros__parameters:
|
||||
left_wheel_joint:
|
||||
has_position_limits: false # Continuous joint
|
||||
has_velocity_limits: true
|
||||
max_velocity: 10.0
|
||||
right_wheel_joint:
|
||||
has_position_limits: false # Continuous joint
|
||||
has_velocity_limits: true
|
||||
max_velocity: 10.0
|
@ -1 +0,0 @@
|
||||
# This is an empty file, so that git commits the folder correctly
|
@ -34,19 +34,19 @@
|
||||
|
||||
<sensor name="laser" type="ray">
|
||||
<pose> 0 0 0 0 0 0 </pose>
|
||||
<visualize>true</visualize>
|
||||
<visualize>false</visualize>
|
||||
<update_rate>10</update_rate>
|
||||
<ray>
|
||||
<scan>
|
||||
<horizontal>
|
||||
<samples>360</samples>
|
||||
<samples>360*5</samples>
|
||||
<min_angle>-3.14</min_angle>
|
||||
<max_angle>3.14</max_angle>
|
||||
</horizontal>
|
||||
</scan>
|
||||
<range>
|
||||
<min>0.3</min>
|
||||
<max>12</max>
|
||||
<max>100</max>
|
||||
</range>
|
||||
</ray>
|
||||
<plugin name="laser_controller" filename="libgazebo_ros_ray_sensor.so">
|
||||
|
@ -5,6 +5,24 @@
|
||||
<xacro:arg name="use_ros2_control" default="true"/>
|
||||
<xacro:arg name="use_sim" default="false"/>
|
||||
|
||||
<xacro:arg name="tube_thickness" default="0.024"/>
|
||||
<xacro:arg name="tube_radius" default="false"/>
|
||||
|
||||
<xacro:arg name="camera_height" default="0.201"/>
|
||||
<xacro:arg name="camera_offset_x" default="0.016"/>
|
||||
|
||||
<xacro:arg name="lidar_height" default="0.201"/>
|
||||
|
||||
<xacro:arg name="wheel_radius" default="0.05"/>
|
||||
<xacro:arg name="wheel_width" default="0.02"/>
|
||||
|
||||
<xacro:arg name="base_height" default="0.1"/>
|
||||
<xacro:arg name="base_width" default="0.2"/>
|
||||
<xacro:arg name="base_length" default="0.2"/>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Include the colours -->
|
||||
<xacro:include filename="colours.xacro" />
|
||||
<!-- Include the inertial macros -->
|
||||
|
@ -25,6 +25,7 @@ def generate_launch_description():
|
||||
package_name='dcaiti_control' #<--- CHANGE ME
|
||||
|
||||
use_ros2_control = LaunchConfiguration('use_ros2_control')
|
||||
world_path='~/.gazebo/models'
|
||||
|
||||
rsp = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource([os.path.join(
|
||||
@ -36,7 +37,7 @@ def generate_launch_description():
|
||||
gazebo = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource([os.path.join(
|
||||
get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py')]),
|
||||
launch_arguments={'params_file': gazebo_params_path}.items()
|
||||
launch_arguments={'params_file': gazebo_params_path, 'world': world_path }.items()
|
||||
)
|
||||
|
||||
# Run the spawner node from the gazebo_ros package. The entity name doesn't really matter if you only have a single robot.
|
||||
|
@ -28,7 +28,6 @@ def generate_launch_description():
|
||||
node_robot_state_publisher = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
output='screen',
|
||||
parameters=[params]
|
||||
)
|
||||
|
||||
|
233
src/dcaiti_navigation/config/ekf.yaml
Normal file
233
src/dcaiti_navigation/config/ekf.yaml
Normal file
@ -0,0 +1,233 @@
|
||||
### ekf config file ###
|
||||
ekf_filter_node:
|
||||
ros__parameters:
|
||||
|
||||
# The frequency, in Hz, at which the filter will output a position estimate. Note that the filter will not begin
|
||||
# computation until it receives at least one message from one of the inputs. It will then run continuously at the
|
||||
# frequency specified here, regardless of whether it receives more measurements. Defaults to 30 if unspecified.
|
||||
frequency: 30.0
|
||||
|
||||
# The period, in seconds, after which we consider a sensor to have timed out. In this event, we carry out a predict
|
||||
# cycle on the EKF without correcting it. This parameter can be thought of as the minimum frequency with which the
|
||||
# filter will generate new output. Defaults to 1 / frequency if not specified.
|
||||
sensor_timeout: 0.1
|
||||
|
||||
# ekf_localization_node and ukf_localization_node both use a 3D omnidirectional motion model. If this parameter is
|
||||
# set to true, no 3D information will be used in your state estimate. Use this if you are operating in a planar
|
||||
# environment and want to ignore the effect of small variations in the ground plane that might otherwise be detected
|
||||
# by, for example, an IMU. Defaults to false if unspecified.
|
||||
two_d_mode: true
|
||||
|
||||
# Use this parameter to provide an offset to the transform generated by ekf_localization_node. This can be used for
|
||||
# future dating the transform, which is required for interaction with some other packages. Defaults to 0.0 if
|
||||
# unspecified.
|
||||
transform_time_offset: 0.0
|
||||
|
||||
# Use this parameter to provide specify how long the tf listener should wait for a transform to become available.
|
||||
# Defaults to 0.0 if unspecified.
|
||||
transform_timeout: 0.0
|
||||
|
||||
# If you're having trouble, try setting this to true, and then echo the /diagnostics_agg topic to see if the node is
|
||||
# unhappy with any settings or data.
|
||||
print_diagnostics: true
|
||||
|
||||
# Debug settings. Not for the faint of heart. Outputs a ludicrous amount of information to the file specified by
|
||||
# debug_out_file. I hope you like matrices! Please note that setting this to true will have strongly deleterious
|
||||
# effects on the performance of the node. Defaults to false if unspecified.
|
||||
debug: false
|
||||
|
||||
# Defaults to "robot_localization_debug.txt" if unspecified. Please specify the full path.
|
||||
debug_out_file: /home/focalfossa/dev_ws/src/basic_mobile_robot/debug_ekf.txt
|
||||
|
||||
# Whether we'll allow old measurements to cause a re-publication of the updated state
|
||||
permit_corrected_publication: false
|
||||
|
||||
# Whether to publish the acceleration state. Defaults to false if unspecified.
|
||||
publish_acceleration: false
|
||||
|
||||
# Whether to broadcast the transformation over the /tf topic. Defaults to true if unspecified.
|
||||
publish_tf: true
|
||||
|
||||
# REP-105 (http://www.ros.org/reps/rep-0105.html) specifies four principal coordinate frames: base_link, odom, map, and
|
||||
# earth. base_link is the coordinate frame that is affixed to the robot. Both odom and map are world-fixed frames.
|
||||
# The robot's position in the odom frame will drift over time, but is accurate in the short term and should be
|
||||
# continuous. The odom frame is therefore the best frame for executing local motion plans. The map frame, like the odom
|
||||
# frame, is a world-fixed coordinate frame, and while it contains the most globally accurate position estimate for your
|
||||
# robot, it is subject to discrete jumps, e.g., due to the fusion of GPS data or a correction from a map-based
|
||||
# localization node. The earth frame is used to relate multiple map frames by giving them a common reference frame.
|
||||
# ekf_localization_node and ukf_localization_node are not concerned with the earth frame.
|
||||
# Here is how to use the following settings:
|
||||
# 1. Set the map_frame, odom_frame, and base_link frames to the appropriate frame names for your system.
|
||||
# 1a. If your system does not have a map_frame, just remove it, and make sure "world_frame" is set to the value of
|
||||
# odom_frame.
|
||||
# 2. If you are fusing continuous position data such as wheel encoder odometry, visual odometry, or IMU data, set
|
||||
# "world_frame" to your odom_frame value. This is the default behavior for robot_localization's state estimation nodes.
|
||||
# 3. If you are fusing global absolute position data that is subject to discrete jumps (e.g., GPS or position updates
|
||||
# from landmark observations) then:
|
||||
# 3a. Set your "world_frame" to your map_frame value
|
||||
# 3b. MAKE SURE something else is generating the odom->base_link transform. Note that this can even be another state
|
||||
# estimation node from robot_localization! However, that instance should *not* fuse the global data.
|
||||
map_frame: map # Defaults to "map" if unspecified
|
||||
odom_frame: odom # Defaults to "odom" if unspecified
|
||||
base_link_frame: base_footprint # Defaults to "base_link" if unspecified
|
||||
world_frame: odom # Defaults to the value of odom_frame if unspecified
|
||||
|
||||
# The filter accepts an arbitrary number of inputs from each input message type (nav_msgs/Odometry,
|
||||
# geometry_msgs/PoseWithCovarianceStamped, geometry_msgs/TwistWithCovarianceStamped,
|
||||
# sensor_msgs/Imu). To add an input, simply append the next number in the sequence to its "base" name, e.g., odom0,
|
||||
# odom1, twist0, twist1, imu0, imu1, imu2, etc. The value should be the topic name. These parameters obviously have no
|
||||
# default values, and must be specified.
|
||||
odom0: wheel/odometry
|
||||
|
||||
# Each sensor reading updates some or all of the filter's state. These options give you greater control over which
|
||||
# values from each measurement are fed to the filter. For example, if you have an odometry message as input, but only
|
||||
# want to use its Z position value, then set the entire vector to false, except for the third entry. The order of the
|
||||
# values is x, y, z, roll, pitch, yaw, vx, vy, vz, vroll, vpitch, vyaw, ax, ay, az. Note that not some message types
|
||||
# do not provide some of the state variables estimated by the filter. For example, a TwistWithCovarianceStamped message
|
||||
# has no pose information, so the first six values would be meaningless in that case. Each vector defaults to all false
|
||||
# if unspecified, effectively making this parameter required for each sensor.
|
||||
odom0_config: [true, true, true,
|
||||
false, false, false,
|
||||
true, true, true,
|
||||
false, false, true,
|
||||
false, false, false]
|
||||
|
||||
# [x_pos , y_pos , z_pos,
|
||||
# roll , pitch , yaw,
|
||||
# x_vel , y_vel , z_vel,
|
||||
# roll_vel, pitch_vel, yaw_vel,
|
||||
# x_accel , y_accel , z_accel]
|
||||
|
||||
# If you have high-frequency data or are running with a low frequency parameter value, then you may want to increase
|
||||
# the size of the subscription queue so that more measurements are fused.
|
||||
odom0_queue_size: 2
|
||||
|
||||
# [ADVANCED] Large messages in ROS can exhibit strange behavior when they arrive at a high frequency. This is a result
|
||||
# of Nagle's algorithm. This option tells the ROS subscriber to use the tcpNoDelay option, which disables Nagle's
|
||||
# algorithm.
|
||||
odom0_nodelay: false
|
||||
|
||||
# [ADVANCED] When measuring one pose variable with two sensors, a situation can arise in which both sensors under-
|
||||
# report their covariances. This can lead to the filter rapidly jumping back and forth between each measurement as they
|
||||
# arrive. In these cases, it often makes sense to (a) correct the measurement covariances, or (b) if velocity is also
|
||||
# measured by one of the sensors, let one sensor measure pose, and the other velocity. However, doing (a) or (b) isn't
|
||||
# always feasible, and so we expose the differential parameter. When differential mode is enabled, all absolute pose
|
||||
# data is converted to velocity data by differentiating the absolute pose measurements. These velocities are then
|
||||
# integrated as usual. NOTE: this only applies to sensors that provide pose measurements; setting differential to true
|
||||
# for twist measurements has no effect.
|
||||
odom0_differential: false
|
||||
|
||||
# [ADVANCED] When the node starts, if this parameter is true, then the first measurement is treated as a "zero point"
|
||||
# for all future measurements. While you can achieve the same effect with the differential paremeter, the key
|
||||
# difference is that the relative parameter doesn't cause the measurement to be converted to a velocity before
|
||||
# integrating it. If you simply want your measurements to start at 0 for a given sensor, set this to true.
|
||||
odom0_relative: false
|
||||
|
||||
# [ADVANCED] If your data is subject to outliers, use these threshold settings, expressed as Mahalanobis distances, to
|
||||
# control how far away from the current vehicle state a sensor measurement is permitted to be. Each defaults to
|
||||
# numeric_limits<double>::max() if unspecified. It is strongly recommended that these parameters be removed if not
|
||||
# required. Data is specified at the level of pose and twist variables, rather than for each variable in isolation.
|
||||
# For messages that have both pose and twist data, the parameter specifies to which part of the message we are applying
|
||||
# the thresholds.
|
||||
odom0_pose_rejection_threshold: 5.0
|
||||
odom0_twist_rejection_threshold: 1.0
|
||||
|
||||
imu0: imu/data
|
||||
imu0_config: [false, false, false,
|
||||
true, true, true,
|
||||
false, false, false,
|
||||
true, true, true,
|
||||
true, true, true]
|
||||
|
||||
# [x_pos , y_pos , z_pos,
|
||||
# roll , pitch , yaw,
|
||||
# x_vel , y_vel , z_vel,
|
||||
# roll_vel, pitch_vel, yaw_vel,
|
||||
# x_accel , y_accel , z_accel]
|
||||
|
||||
imu0_nodelay: false
|
||||
imu0_differential: false
|
||||
imu0_relative: true
|
||||
imu0_queue_size: 5
|
||||
imu0_pose_rejection_threshold: 0.8 # Note the difference in parameter names
|
||||
imu0_twist_rejection_threshold: 0.8 #
|
||||
imu0_linear_acceleration_rejection_threshold: 0.8 #
|
||||
|
||||
# [ADVANCED] Some IMUs automatically remove acceleration due to gravity, and others don't. If yours doesn't, please set
|
||||
# this to true, and *make sure* your data conforms to REP-103, specifically, that the data is in ENU frame.
|
||||
imu0_remove_gravitational_acceleration: true
|
||||
|
||||
# [ADVANCED] The EKF and UKF models follow a standard predict/correct cycle. During prediction, if there is no
|
||||
# acceleration reference, the velocity at time t+1 is simply predicted to be the same as the velocity at time t. During
|
||||
# correction, this predicted value is fused with the measured value to produce the new velocity estimate. This can be
|
||||
# problematic, as the final velocity will effectively be a weighted average of the old velocity and the new one. When
|
||||
# this velocity is the integrated into a new pose, the result can be sluggish covergence. This effect is especially
|
||||
# noticeable with LIDAR data during rotations. To get around it, users can try inflating the process_noise_covariance
|
||||
# for the velocity variable in question, or decrease the variance of the variable in question in the measurement
|
||||
# itself. In addition, users can also take advantage of the control command being issued to the robot at the time we
|
||||
# make the prediction. If control is used, it will get converted into an acceleration term, which will be used during
|
||||
# predicition. Note that if an acceleration measurement for the variable in question is available from one of the
|
||||
# inputs, the control term will be ignored.
|
||||
# Whether or not we use the control input during predicition. Defaults to false.
|
||||
use_control: false
|
||||
# Whether the input (assumed to be cmd_vel) is a geometry_msgs/Twist or geometry_msgs/TwistStamped message. Defaults to
|
||||
# false.
|
||||
stamped_control: false
|
||||
# The last issued control command will be used in prediction for this period. Defaults to 0.2.
|
||||
control_timeout: 0.2
|
||||
# Which velocities are being controlled. Order is vx, vy, vz, vroll, vpitch, vyaw.
|
||||
control_config: [true, false, false, false, false, true]
|
||||
# Places limits on how large the acceleration term will be. Should match your robot's kinematics.
|
||||
acceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 3.4]
|
||||
# Acceleration and deceleration limits are not always the same for robots.
|
||||
deceleration_limits: [1.3, 0.0, 0.0, 0.0, 0.0, 4.5]
|
||||
# If your robot cannot instantaneously reach its acceleration limit, the permitted change can be controlled with these
|
||||
# gains
|
||||
acceleration_gains: [0.8, 0.0, 0.0, 0.0, 0.0, 0.9]
|
||||
# If your robot cannot instantaneously reach its deceleration limit, the permitted change can be controlled with these
|
||||
# gains
|
||||
deceleration_gains: [1.0, 0.0, 0.0, 0.0, 0.0, 1.0]
|
||||
# [ADVANCED] The process noise covariance matrix can be difficult to tune, and can vary for each application, so it is
|
||||
# exposed as a configuration parameter. This matrix represents the noise we add to the total error after each
|
||||
# prediction step. The better the omnidirectional motion model matches your system, the smaller these values can be.
|
||||
# However, if users find that a given variable is slow to converge, one approach is to increase the
|
||||
# process_noise_covariance diagonal value for the variable in question, which will cause the filter's predicted error
|
||||
# to be larger, which will cause the filter to trust the incoming measurement more during correction. The values are
|
||||
# ordered as x, y, z, roll, pitch, yaw, vx, vy, vz, vroll, vpitch, vyaw, ax, ay, az. Defaults to the matrix below if
|
||||
# unspecified.
|
||||
process_noise_covariance: [0.05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.06, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.03, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.03, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.06, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.025, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.025, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.04, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.015]
|
||||
# [ADVANCED] This represents the initial value for the state estimate error covariance matrix. Setting a diagonal
|
||||
# value (variance) to a large value will result in rapid convergence for initial measurements of the variable in
|
||||
# question. Users should take care not to use large values for variables that will not be measured directly. The values
|
||||
# are ordered as x, y, z, roll, pitch, yaw, vx, vy, vz, vroll, vpitch, vyaw, ax, ay, az. Defaults to the matrix below
|
||||
#if unspecified.
|
||||
initial_estimate_covariance: [1e-9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1e-9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1e-9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1e-9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 1e-9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 1e-9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-9, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-9, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-9, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-9, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-9, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-9]
|
||||
|
11
src/dcaiti_navigation/config/nav2.yml
Normal file
11
src/dcaiti_navigation/config/nav2.yml
Normal file
@ -0,0 +1,11 @@
|
||||
planner_server:
|
||||
ros__parameters:
|
||||
planner_plugins: ['GridBased']
|
||||
GridBased:
|
||||
plugin: 'nav2_navfn_planner/NavfnPlanner'
|
||||
|
||||
controller_server:
|
||||
ros__parameters:
|
||||
controller_plugins: ["FollowPath"]
|
||||
FollowPath:
|
||||
plugin: "dwb_core::DWBLocalPlanner"
|
0
src/dcaiti_navigation/dcaiti_navigation/__init__.py
Normal file
0
src/dcaiti_navigation/dcaiti_navigation/__init__.py
Normal file
144
src/dcaiti_navigation/launch/start_nav2.py
Normal file
144
src/dcaiti_navigation/launch/start_nav2.py
Normal file
@ -0,0 +1,144 @@
|
||||
# Author: Addison Sears-Collins
|
||||
# Date: September 2, 2021
|
||||
# Description: Launch a basic mobile robot using the ROS 2 Navigation Stack
|
||||
# https://automaticaddison.com
|
||||
|
||||
import os
|
||||
from ament_index_python import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.substitutions import Command, LaunchConfiguration, PythonExpression
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
|
||||
def generate_launch_description():
|
||||
|
||||
# Set the path to different files and folders.
|
||||
pkg_share = get_package_share_directory('dcaiti_control')
|
||||
world_file_name = 'basic_mobile_bot_world/smalltown.world'
|
||||
world_path = os.path.join(pkg_share, 'worlds', world_file_name)
|
||||
static_map_path = os.path.join(pkg_share, 'maps', 'smalltown_world.yaml')
|
||||
nav2_params_path = os.path.join(pkg_share, 'params', 'nav2_params.yaml')
|
||||
behavior_tree_xml_path = os.path.join(get_package_share_directory('nav2_bt_navigator'), 'behavior_trees', 'navigate_w_replanning_and_recovery.xml')
|
||||
|
||||
# Launch configuration variables specific to simulation
|
||||
autostart = LaunchConfiguration('autostart')
|
||||
default_bt_xml_filename = LaunchConfiguration('default_bt_xml_filename')
|
||||
map_yaml_file = LaunchConfiguration('map')
|
||||
namespace = LaunchConfiguration('namespace')
|
||||
params_file = LaunchConfiguration('params_file')
|
||||
slam = LaunchConfiguration('slam')
|
||||
use_namespace = LaunchConfiguration('use_namespace')
|
||||
use_sim_time = LaunchConfiguration('use_sim_time')
|
||||
|
||||
# Map fully qualified names to relative ones so the node's namespace can be prepended.
|
||||
# In case of the transforms (tf), currently, there doesn't seem to be a better alternative
|
||||
# https://github.com/ros/geometry2/issues/32
|
||||
# https://github.com/ros/robot_state_publisher/pull/30
|
||||
# TODO(orduno) Substitute with `PushNodeRemapping`
|
||||
# https://github.com/ros2/launch_ros/issues/56
|
||||
remappings = [('/tf', 'tf'),
|
||||
('/tf_static', 'tf_static')]
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_namespace_cmd = DeclareLaunchArgument(
|
||||
name='namespace',
|
||||
default_value='',
|
||||
description='Top-level namespace')
|
||||
|
||||
declare_use_namespace_cmd = DeclareLaunchArgument(
|
||||
name='use_namespace',
|
||||
default_value='False',
|
||||
description='Whether to apply a namespace to the navigation stack')
|
||||
|
||||
declare_autostart_cmd = DeclareLaunchArgument(
|
||||
name='autostart',
|
||||
default_value='true',
|
||||
description='Automatically startup the nav2 stack')
|
||||
|
||||
declare_bt_xml_cmd = DeclareLaunchArgument(
|
||||
name='default_bt_xml_filename',
|
||||
default_value=behavior_tree_xml_path,
|
||||
description='Full path to the behavior tree xml file to use')
|
||||
|
||||
declare_map_yaml_cmd = DeclareLaunchArgument(
|
||||
name='map',
|
||||
default_value=static_map_path,
|
||||
description='Full path to map file to load')
|
||||
|
||||
declare_params_file_cmd = DeclareLaunchArgument(
|
||||
name='params_file',
|
||||
default_value=nav2_params_path,
|
||||
description='Full path to the ROS2 parameters file to use for all launched nodes')
|
||||
|
||||
declare_simulator_cmd = DeclareLaunchArgument(
|
||||
name='headless',
|
||||
default_value='False',
|
||||
description='Whether to execute gzclient')
|
||||
|
||||
declare_slam_cmd = DeclareLaunchArgument(
|
||||
name='slam',
|
||||
default_value='False',
|
||||
description='Whether to run SLAM')
|
||||
|
||||
declare_use_robot_state_pub_cmd = DeclareLaunchArgument(
|
||||
name='use_robot_state_pub',
|
||||
default_value='True',
|
||||
description='Whether to start the robot state publisher')
|
||||
|
||||
declare_use_rviz_cmd = DeclareLaunchArgument(
|
||||
name='use_rviz',
|
||||
default_value='True',
|
||||
description='Whether to start RVIZ')
|
||||
|
||||
declare_use_sim_time_cmd = DeclareLaunchArgument(
|
||||
name='use_sim_time',
|
||||
default_value='True',
|
||||
description='Use simulation (Gazebo) clock if true')
|
||||
|
||||
declare_use_simulator_cmd = DeclareLaunchArgument(
|
||||
name='use_simulator',
|
||||
default_value='True',
|
||||
description='Whether to start the simulator')
|
||||
|
||||
declare_world_cmd = DeclareLaunchArgument(
|
||||
name='world',
|
||||
default_value=world_path,
|
||||
description='Full path to the world model file to load')
|
||||
|
||||
# Specify the actions
|
||||
|
||||
# Launch the ROS 2 Navigation Stack
|
||||
start_ros2_navigation_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(get_package_share_directory('nav2'),'launch','bringup_launch.py'),
|
||||
launch_arguments = {'namespace': namespace,
|
||||
'use_namespace': use_namespace,
|
||||
'slam': slam,
|
||||
'map': map_yaml_file,
|
||||
'use_sim_time': use_sim_time,
|
||||
'params_file': params_file,
|
||||
'default_bt_xml_filename': default_bt_xml_filename,
|
||||
'autostart': autostart}.items())
|
||||
|
||||
# Create the launch description and populate
|
||||
ld = LaunchDescription()
|
||||
|
||||
# Declare the launch options
|
||||
ld.add_action(declare_namespace_cmd)
|
||||
ld.add_action(declare_use_namespace_cmd)
|
||||
ld.add_action(declare_autostart_cmd)
|
||||
ld.add_action(declare_bt_xml_cmd)
|
||||
ld.add_action(declare_map_yaml_cmd)
|
||||
ld.add_action(declare_params_file_cmd)
|
||||
ld.add_action(declare_simulator_cmd)
|
||||
ld.add_action(declare_slam_cmd)
|
||||
ld.add_action(declare_use_robot_state_pub_cmd)
|
||||
ld.add_action(declare_use_rviz_cmd)
|
||||
ld.add_action(declare_use_sim_time_cmd)
|
||||
ld.add_action(declare_use_simulator_cmd)
|
||||
ld.add_action(declare_world_cmd)
|
||||
|
||||
# Add any actions
|
||||
ld.add_action(start_ros2_navigation_cmd)
|
||||
|
||||
return ld
|
BIN
src/dcaiti_navigation/maps/my_map.pgm
Normal file
BIN
src/dcaiti_navigation/maps/my_map.pgm
Normal file
Binary file not shown.
7
src/dcaiti_navigation/maps/my_map.yaml
Normal file
7
src/dcaiti_navigation/maps/my_map.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
image: my_map.pgm
|
||||
mode: trinary
|
||||
resolution: 0.05
|
||||
origin: [-18.7, -17.5, 0]
|
||||
negate: 0
|
||||
occupied_thresh: 0.65
|
||||
free_thresh: 0.25
|
5
src/dcaiti_navigation/maps/smalltown_world.pgm
Normal file
5
src/dcaiti_navigation/maps/smalltown_world.pgm
Normal file
File diff suppressed because one or more lines are too long
6
src/dcaiti_navigation/maps/smalltown_world.yaml
Normal file
6
src/dcaiti_navigation/maps/smalltown_world.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
image: smalltown_world.pgm
|
||||
resolution: 0.050000
|
||||
origin: [-51.224998, -51.224998, 0.000000]
|
||||
negate: 0
|
||||
occupied_thresh: 0.65
|
||||
free_thresh: 0.196
|
18
src/dcaiti_navigation/package.xml
Normal file
18
src/dcaiti_navigation/package.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>dcaiti_navigation</name>
|
||||
<version>0.0.0</version>
|
||||
<description>TODO: Package description</description>
|
||||
<maintainer email="you@example.com">korjakow</maintainer>
|
||||
<license>TODO: License declaration</license>
|
||||
|
||||
<test_depend>ament_copyright</test_depend>
|
||||
<test_depend>ament_flake8</test_depend>
|
||||
<test_depend>ament_pep257</test_depend>
|
||||
<test_depend>python3-pytest</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_python</build_type>
|
||||
</export>
|
||||
</package>
|
403
src/dcaiti_navigation/params/nav2_params.yaml
Normal file
403
src/dcaiti_navigation/params/nav2_params.yaml
Normal file
@ -0,0 +1,403 @@
|
||||
amcl:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
alpha1: 0.002
|
||||
alpha2: 0.002
|
||||
alpha3: 0.002
|
||||
alpha4: 0.002
|
||||
alpha5: 0.002
|
||||
base_frame_id: "base_link"
|
||||
beam_skip_distance: 0.5
|
||||
beam_skip_error_threshold: 0.9
|
||||
beam_skip_threshold: 0.3
|
||||
do_beamskip: false
|
||||
global_frame_id: "map"
|
||||
initial_pose: [0.0, 0.0, 0.0, 0.0]
|
||||
lambda_short: 0.1
|
||||
laser_likelihood_max_dist: 2.0
|
||||
laser_max_range: 100.0
|
||||
laser_min_range: -1.0
|
||||
laser_model_type: "likelihood_field"
|
||||
max_beams: 60
|
||||
max_particles: 2000
|
||||
min_particles: 500
|
||||
odom_frame_id: "odom"
|
||||
pf_err: 0.05
|
||||
pf_z: 0.99
|
||||
recovery_alpha_fast: 0.0
|
||||
recovery_alpha_slow: 0.0
|
||||
resample_interval: 1
|
||||
robot_model_type: "differential"
|
||||
save_pose_rate: 0.5
|
||||
set_initial_pose: False
|
||||
sigma_hit: 0.2
|
||||
tf_broadcast: true
|
||||
transform_tolerance: 1.0
|
||||
update_min_a: 0.2
|
||||
update_min_d: 0.25
|
||||
z_hit: 0.5
|
||||
z_max: 0.05
|
||||
z_rand: 0.5
|
||||
z_short: 0.05
|
||||
scan_topic: scan
|
||||
|
||||
amcl_map_client:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
|
||||
amcl_rclcpp_node:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
|
||||
bt_navigator:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
global_frame: map
|
||||
robot_base_frame: base_link
|
||||
odom_topic: /diff_cont/odom #/wheel/odometry
|
||||
bt_loop_duration: 10
|
||||
default_server_timeout: 20
|
||||
enable_groot_monitoring: True
|
||||
groot_zmq_publisher_port: 1666
|
||||
groot_zmq_server_port: 1667
|
||||
# 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
|
||||
# nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
|
||||
# nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
|
||||
# They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
|
||||
plugin_lib_names:
|
||||
- nav2_compute_path_to_pose_action_bt_node
|
||||
- nav2_compute_path_through_poses_action_bt_node
|
||||
- nav2_follow_path_action_bt_node
|
||||
- nav2_back_up_action_bt_node
|
||||
- nav2_spin_action_bt_node
|
||||
- nav2_wait_action_bt_node
|
||||
- nav2_clear_costmap_service_bt_node
|
||||
- nav2_is_stuck_condition_bt_node
|
||||
- nav2_goal_reached_condition_bt_node
|
||||
- nav2_goal_updated_condition_bt_node
|
||||
- nav2_initial_pose_received_condition_bt_node
|
||||
- nav2_reinitialize_global_localization_service_bt_node
|
||||
- nav2_rate_controller_bt_node
|
||||
- nav2_distance_controller_bt_node
|
||||
- nav2_speed_controller_bt_node
|
||||
- nav2_truncate_path_action_bt_node
|
||||
- nav2_goal_updater_node_bt_node
|
||||
- nav2_recovery_node_bt_node
|
||||
- nav2_pipeline_sequence_bt_node
|
||||
- nav2_round_robin_node_bt_node
|
||||
- nav2_transform_available_condition_bt_node
|
||||
- nav2_time_expired_condition_bt_node
|
||||
- nav2_distance_traveled_condition_bt_node
|
||||
- nav2_single_trigger_bt_node
|
||||
- nav2_is_battery_low_condition_bt_node
|
||||
- nav2_navigate_through_poses_action_bt_node
|
||||
- nav2_navigate_to_pose_action_bt_node
|
||||
- nav2_remove_passed_goals_action_bt_node
|
||||
- nav2_planner_selector_bt_node
|
||||
- nav2_controller_selector_bt_node
|
||||
- nav2_goal_checker_selector_bt_node
|
||||
|
||||
bt_navigator_rclcpp_node:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
|
||||
controller_server:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
controller_frequency: 2.0
|
||||
min_x_velocity_threshold: 0.001
|
||||
min_y_velocity_threshold: 0.5
|
||||
min_theta_velocity_threshold: 0.001
|
||||
failure_tolerance: 0.3
|
||||
progress_checker_plugin: "progress_checker"
|
||||
goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
|
||||
controller_plugins: ["FollowPath"]
|
||||
|
||||
# Progress checker parameters
|
||||
progress_checker:
|
||||
plugin: "nav2_controller::SimpleProgressChecker"
|
||||
required_movement_radius: 0.5
|
||||
movement_time_allowance: 10.0
|
||||
# Goal checker parameters
|
||||
#precise_goal_checker:
|
||||
# plugin: "nav2_controller::SimpleGoalChecker"
|
||||
# xy_goal_tolerance: 0.25
|
||||
# yaw_goal_tolerance: 0.25
|
||||
# stateful: True
|
||||
general_goal_checker:
|
||||
stateful: True
|
||||
plugin: "nav2_controller::SimpleGoalChecker"
|
||||
xy_goal_tolerance: 0.25
|
||||
yaw_goal_tolerance: 0.25
|
||||
# DWB parameters
|
||||
FollowPath:
|
||||
plugin: "dwb_core::DWBLocalPlanner"
|
||||
debug_trajectory_details: True
|
||||
min_vel_x: 0.0
|
||||
min_vel_y: 0.0
|
||||
max_vel_x: 0.26
|
||||
max_vel_y: 0.0
|
||||
max_vel_theta: 1.0
|
||||
min_speed_xy: 0.0
|
||||
max_speed_xy: 0.26
|
||||
min_speed_theta: 0.0
|
||||
# Add high threshold velocity for turtlebot 3 issue.
|
||||
# https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
|
||||
acc_lim_x: 2.5
|
||||
acc_lim_y: 0.0
|
||||
acc_lim_theta: 3.2
|
||||
decel_lim_x: -2.5
|
||||
decel_lim_y: 0.0
|
||||
decel_lim_theta: -3.2
|
||||
vx_samples: 20
|
||||
vy_samples: 5
|
||||
vtheta_samples: 20
|
||||
sim_time: 1.7
|
||||
linear_granularity: 0.05
|
||||
angular_granularity: 0.025
|
||||
transform_tolerance: 0.2
|
||||
xy_goal_tolerance: 0.25
|
||||
trans_stopped_velocity: 0.25
|
||||
short_circuit_trajectory_evaluation: True
|
||||
stateful: True
|
||||
critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
|
||||
BaseObstacle.scale: 0.02
|
||||
PathAlign.scale: 32.0
|
||||
PathAlign.forward_point_distance: 0.1
|
||||
GoalAlign.scale: 24.0
|
||||
GoalAlign.forward_point_distance: 0.1
|
||||
PathDist.scale: 32.0
|
||||
GoalDist.scale: 24.0
|
||||
RotateToGoal.scale: 32.0
|
||||
RotateToGoal.slowing_factor: 5.0
|
||||
RotateToGoal.lookahead_time: -1.0
|
||||
|
||||
controller_server_rclcpp_node:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
|
||||
local_costmap:
|
||||
local_costmap:
|
||||
ros__parameters:
|
||||
update_frequency: 5.0
|
||||
publish_frequency: 2.0
|
||||
global_frame: odom
|
||||
robot_base_frame: base_link
|
||||
use_sim_time: True
|
||||
rolling_window: true
|
||||
width: 10
|
||||
height: 10
|
||||
resolution: 0.05
|
||||
robot_radius: 0.75
|
||||
plugins: ["voxel_layer", "inflation_layer"]
|
||||
inflation_layer:
|
||||
plugin: "nav2_costmap_2d::InflationLayer"
|
||||
cost_scaling_factor: 3.0
|
||||
inflation_radius: 0.55
|
||||
voxel_layer:
|
||||
plugin: "nav2_costmap_2d::VoxelLayer"
|
||||
enabled: True
|
||||
publish_voxel_map: True
|
||||
origin_z: 0.0
|
||||
z_resolution: 0.05
|
||||
z_voxels: 16
|
||||
max_obstacle_height: 2.0
|
||||
mark_threshold: 0
|
||||
observation_sources: scan
|
||||
scan:
|
||||
topic: /scan
|
||||
max_obstacle_height: 2.0
|
||||
clearing: True
|
||||
marking: True
|
||||
data_type: "LaserScan"
|
||||
raytrace_max_range: 10.0
|
||||
raytrace_min_range: 0.0
|
||||
obstacle_max_range: 9.5
|
||||
obstacle_min_range: 0.0
|
||||
static_layer:
|
||||
map_subscribe_transient_local: True
|
||||
always_send_full_costmap: True
|
||||
local_costmap_client:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
local_costmap_rclcpp_node:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
|
||||
global_costmap:
|
||||
global_costmap:
|
||||
ros__parameters:
|
||||
update_frequency: 1.0
|
||||
publish_frequency: 1.0
|
||||
global_frame: map
|
||||
robot_base_frame: base_link
|
||||
use_sim_time: True
|
||||
robot_radius: 0.75
|
||||
resolution: 0.05
|
||||
track_unknown_space: true
|
||||
plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
|
||||
obstacle_layer:
|
||||
plugin: "nav2_costmap_2d::ObstacleLayer"
|
||||
enabled: True
|
||||
observation_sources: scan
|
||||
scan:
|
||||
topic: /scan
|
||||
max_obstacle_height: 2.0
|
||||
clearing: True
|
||||
marking: True
|
||||
data_type: "LaserScan"
|
||||
raytrace_max_range: 10.0
|
||||
raytrace_min_range: 0.0
|
||||
obstacle_max_range: 9.5
|
||||
obstacle_min_range: 0.0
|
||||
static_layer:
|
||||
plugin: "nav2_costmap_2d::StaticLayer"
|
||||
map_subscribe_transient_local: True
|
||||
inflation_layer:
|
||||
plugin: "nav2_costmap_2d::InflationLayer"
|
||||
cost_scaling_factor: 3.0
|
||||
inflation_radius: 0.95
|
||||
always_send_full_costmap: True
|
||||
global_costmap_client:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
global_costmap_rclcpp_node:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
|
||||
map_server:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
yaml_filename: "turtlebot3_world.yaml"
|
||||
|
||||
map_saver:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
save_map_timeout: 5.0
|
||||
free_thresh_default: 0.25
|
||||
occupied_thresh_default: 0.65
|
||||
map_subscribe_transient_local: True
|
||||
|
||||
planner_server:
|
||||
ros__parameters:
|
||||
expected_planner_frequency: 2.0
|
||||
use_sim_time: True
|
||||
planner_plugins: ["GridBased"]
|
||||
GridBased:
|
||||
plugin: "nav2_navfn_planner/NavfnPlanner"
|
||||
tolerance: 0.5
|
||||
use_astar: false
|
||||
allow_unknown: true
|
||||
|
||||
planner_server_rclcpp_node:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
|
||||
recoveries_server:
|
||||
ros__parameters:
|
||||
costmap_topic: local_costmap/costmap_raw
|
||||
footprint_topic: local_costmap/published_footprint
|
||||
cycle_frequency: 10.0
|
||||
recovery_plugins: ["spin", "backup", "wait"]
|
||||
spin:
|
||||
plugin: "nav2_recoveries/Spin"
|
||||
backup:
|
||||
plugin: "nav2_recoveries/BackUp"
|
||||
wait:
|
||||
plugin: "nav2_recoveries/Wait"
|
||||
global_frame: odom
|
||||
robot_base_frame: base_link
|
||||
transform_timeout: 0.1
|
||||
use_sim_time: true
|
||||
simulate_ahead_time: 2.0
|
||||
max_rotational_vel: 1.0
|
||||
min_rotational_vel: 0.4
|
||||
rotational_acc_lim: 3.2
|
||||
|
||||
robot_state_publisher:
|
||||
ros__parameters:
|
||||
use_sim_time: True
|
||||
|
||||
waypoint_follower:
|
||||
ros__parameters:
|
||||
loop_rate: 2
|
||||
stop_on_failure: false
|
||||
waypoint_task_executor_plugin: "wait_at_waypoint"
|
||||
wait_at_waypoint:
|
||||
plugin: "nav2_waypoint_follower::WaitAtWaypoint"
|
||||
enabled: True
|
||||
waypoint_pause_duration: 200
|
||||
|
||||
slam_toolbox:
|
||||
ros__parameters:
|
||||
|
||||
# Plugin params
|
||||
solver_plugin: solver_plugins::CeresSolver
|
||||
ceres_linear_solver: SPARSE_NORMAL_CHOLESKY
|
||||
ceres_preconditioner: SCHUR_JACOBI
|
||||
ceres_trust_strategy: LEVENBERG_MARQUARDT
|
||||
ceres_dogleg_type: TRADITIONAL_DOGLEG
|
||||
ceres_loss_function: None
|
||||
|
||||
# ROS Parameters
|
||||
odom_frame: odom
|
||||
map_frame: map
|
||||
base_frame: base_link
|
||||
scan_topic: /scan
|
||||
mode: mapping #localization
|
||||
|
||||
# if you'd like to immediately start continuing a map at a given pose
|
||||
# or at the dock, but they are mutually exclusive, if pose is given
|
||||
# will use pose
|
||||
#map_file_name: test_steve
|
||||
#map_start_pose: [0.0, 0.0, 0.0]
|
||||
#map_start_at_dock: true
|
||||
|
||||
debug_logging: false
|
||||
throttle_scans: 1
|
||||
transform_publish_period: 0.02 #if 0 never publishes odometry
|
||||
map_update_interval: 5.0
|
||||
resolution: 0.05
|
||||
max_laser_range: 20.0 #for rastering images
|
||||
minimum_time_interval: 0.5
|
||||
transform_timeout: 0.2
|
||||
tf_buffer_duration: 30.
|
||||
stack_size_to_use: 40000000 #// program needs a larger stack size to serialize large maps
|
||||
enable_interactive_mode: true
|
||||
|
||||
# General Parameters
|
||||
use_scan_matching: true
|
||||
use_scan_barycenter: true
|
||||
minimum_travel_distance: 0.5
|
||||
minimum_travel_heading: 0.5
|
||||
scan_buffer_size: 10
|
||||
scan_buffer_maximum_scan_distance: 10.0
|
||||
link_match_minimum_response_fine: 0.1
|
||||
link_scan_maximum_distance: 1.5
|
||||
loop_search_maximum_distance: 3.0
|
||||
do_loop_closing: true
|
||||
loop_match_minimum_chain_size: 10
|
||||
loop_match_maximum_variance_coarse: 3.0
|
||||
loop_match_minimum_response_coarse: 0.35
|
||||
loop_match_minimum_response_fine: 0.45
|
||||
|
||||
# Correlation Parameters - Correlation Parameters
|
||||
correlation_search_space_dimension: 0.5
|
||||
correlation_search_space_resolution: 0.01
|
||||
correlation_search_space_smear_deviation: 0.1
|
||||
|
||||
# Correlation Parameters - Loop Closure Parameters
|
||||
loop_search_space_dimension: 8.0
|
||||
loop_search_space_resolution: 0.05
|
||||
loop_search_space_smear_deviation: 0.03
|
||||
|
||||
# Scan Matcher Parameters
|
||||
distance_variance_penalty: 0.5
|
||||
angle_variance_penalty: 1.0
|
||||
|
||||
fine_search_angle_offset: 0.00349
|
||||
coarse_search_angle_offset: 0.349
|
||||
coarse_angle_resolution: 0.0349
|
||||
minimum_angle_penalty: 0.9
|
||||
minimum_distance_penalty: 0.5
|
||||
use_response_expansion: true
|
0
src/dcaiti_navigation/resource/dcaiti_navigation
Normal file
0
src/dcaiti_navigation/resource/dcaiti_navigation
Normal file
4
src/dcaiti_navigation/setup.cfg
Normal file
4
src/dcaiti_navigation/setup.cfg
Normal file
@ -0,0 +1,4 @@
|
||||
[develop]
|
||||
script_dir=$base/lib/dcaiti_navigation
|
||||
[install]
|
||||
install_scripts=$base/lib/dcaiti_navigation
|
28
src/dcaiti_navigation/setup.py
Normal file
28
src/dcaiti_navigation/setup.py
Normal file
@ -0,0 +1,28 @@
|
||||
from glob import glob
|
||||
import os
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
package_name = 'dcaiti_navigation'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='0.0.0',
|
||||
packages=find_packages(exclude=['test']),
|
||||
data_files=[
|
||||
('share/ament_index/resource_index/packages',
|
||||
['resource/' + package_name]),
|
||||
('share/' + package_name, ['package.xml']),
|
||||
(os.path.join('share', package_name), glob('launch/*.py')),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
maintainer='korjakow',
|
||||
maintainer_email='you@example.com',
|
||||
description='TODO: Package description',
|
||||
license='TODO: License declaration',
|
||||
tests_require=['pytest'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
],
|
||||
},
|
||||
)
|
25
src/dcaiti_navigation/test/test_copyright.py
Normal file
25
src/dcaiti_navigation/test/test_copyright.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# 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 ament_copyright.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
# Remove the `skip` decorator once the source file(s) have a copyright header
|
||||
@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
|
||||
@pytest.mark.copyright
|
||||
@pytest.mark.linter
|
||||
def test_copyright():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found errors'
|
25
src/dcaiti_navigation/test/test_flake8.py
Normal file
25
src/dcaiti_navigation/test/test_flake8.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright 2017 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# 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 ament_flake8.main import main_with_errors
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.flake8
|
||||
@pytest.mark.linter
|
||||
def test_flake8():
|
||||
rc, errors = main_with_errors(argv=[])
|
||||
assert rc == 0, \
|
||||
'Found %d code style errors / warnings:\n' % len(errors) + \
|
||||
'\n'.join(errors)
|
23
src/dcaiti_navigation/test/test_pep257.py
Normal file
23
src/dcaiti_navigation/test/test_pep257.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# 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 ament_pep257.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.linter
|
||||
@pytest.mark.pep257
|
||||
def test_pep257():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found code style errors / warnings'
|
4084
src/dcaiti_navigation/worlds/basic_mobile_bot_world/smalltown.world
Normal file
4084
src/dcaiti_navigation/worlds/basic_mobile_bot_world/smalltown.world
Normal file
File diff suppressed because it is too large
Load Diff
1
src/dcaitirobot/.gitignore
vendored
1
src/dcaitirobot/.gitignore
vendored
@ -3,7 +3,6 @@ logs/
|
||||
install/
|
||||
build/
|
||||
bin/
|
||||
lib/
|
||||
log/
|
||||
msg_gen/
|
||||
srv_gen/
|
||||
|
6
src/dcaitirobot/config/slam.yml
Normal file
6
src/dcaitirobot/config/slam.yml
Normal file
@ -0,0 +1,6 @@
|
||||
slam_toolbox:
|
||||
ros__parameters:
|
||||
base_frame: base_link
|
||||
map_update_interval: 1.0
|
||||
scan_topic: /scan_no_nan
|
||||
odom_topic: /odom
|
42
src/dcaitirobot/dcaitirobot/handle_nan_scan.py
Normal file
42
src/dcaitirobot/dcaitirobot/handle_nan_scan.py
Normal file
@ -0,0 +1,42 @@
|
||||
import numpy as np
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from sensor_msgs.msg import LaserScan
|
||||
import geometry_msgs.msg as gm
|
||||
|
||||
|
||||
class TF2PublisherNode(Node):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('nanhandler')
|
||||
|
||||
self.create_subscription(LaserScan, "/scan", self.callback, 1)
|
||||
self.pub = self.create_publisher(LaserScan, "/scan_no_nan", 1)
|
||||
def callback(self, msg):
|
||||
print(msg.ranges[0])
|
||||
|
||||
msg.ranges[0] = float("inf")
|
||||
msg.intensities[0] = 0.0
|
||||
|
||||
msg.ranges.append(float("inf"))
|
||||
msg.intensities.append(0.0)
|
||||
# ranges = np.array(msg.ranges)
|
||||
# mask_inf_dist = np.isinf(ranges)
|
||||
# print(np.array(msg.intensities)[mask_inf_dist])
|
||||
|
||||
self.pub.publish(msg)
|
||||
|
||||
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
|
||||
tf2_publisher = TF2PublisherNode()
|
||||
rclpy.spin(tf2_publisher)
|
||||
|
||||
tf2_publisher.destroy_node()
|
||||
rclpy.shutdown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
65
src/dcaitirobot/dcaitirobot/scale_odom.py
Normal file
65
src/dcaitirobot/dcaitirobot/scale_odom.py
Normal file
@ -0,0 +1,65 @@
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
|
||||
from geometry_msgs.msg import Twist
|
||||
from std_msgs.msg import String
|
||||
from nav_msgs.msg import Odometry
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
SCALE_FACTOR = 1.0
|
||||
|
||||
|
||||
|
||||
class TwistPublisher(Node):
|
||||
def __init__(
|
||||
self,
|
||||
scale_factor=20.0
|
||||
):
|
||||
super().__init__("twist_publisher")
|
||||
|
||||
self.scale_factor = scale_factor
|
||||
|
||||
self.publisher_ = self.create_publisher(
|
||||
Odometry, "/odom_scaled", 1
|
||||
)
|
||||
|
||||
self.create_subscription(Odometry, "/odom", self.odom_callback, 1)
|
||||
|
||||
def odom_callback(self, odom_message:Odometry):
|
||||
odom_message.twist.twist.linear.x *= self.scale_factor
|
||||
odom_message.twist.twist.linear.y *= self.scale_factor
|
||||
odom_message.twist.twist.linear.z *= self.scale_factor
|
||||
odom_message.twist.twist.angular.x *= self.scale_factor
|
||||
odom_message.twist.twist.angular.y *= self.scale_factor
|
||||
odom_message.twist.twist.angular.z *= self.scale_factor
|
||||
odom_message.pose.pose.position.x *= self.scale_factor
|
||||
odom_message.pose.pose.position.y *= self.scale_factor
|
||||
odom_message.pose.pose.position.z *= self.scale_factor
|
||||
odom_message.pose.pose.orientation.x *= self.scale_factor
|
||||
odom_message.pose.pose.orientation.y *= self.scale_factor
|
||||
odom_message.pose.pose.orientation.z *= self.scale_factor
|
||||
|
||||
self.publisher_.publish(odom_message)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def main(args=None):
|
||||
rclpy.init(args=args)
|
||||
|
||||
minimal_publisher = TwistPublisher(
|
||||
SCALE_FACTOR
|
||||
)
|
||||
|
||||
rclpy.spin(minimal_publisher)
|
||||
|
||||
minimal_publisher.destroy_node()
|
||||
rclpy.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -12,16 +12,14 @@ class TF2PublisherNode(Node):
|
||||
|
||||
self.timer = self.create_timer(0.1, self.publish_identity_transform)
|
||||
|
||||
self.x = 0.0
|
||||
|
||||
def publish_identity_transform(self):
|
||||
def send_parent_child_identity_tf(self, parent, child):
|
||||
transform_stamped = gm.TransformStamped()
|
||||
transform_stamped.header.stamp = self.get_clock().now().to_msg()
|
||||
transform_stamped.header.frame_id = 'odom'
|
||||
transform_stamped.child_frame_id = 'base_link'
|
||||
transform_stamped.header.frame_id = parent
|
||||
transform_stamped.child_frame_id = child
|
||||
|
||||
# Identity transform (no translation, no rotation)
|
||||
transform_stamped.transform.translation.x = self.x
|
||||
transform_stamped.transform.translation.x = 0.0
|
||||
transform_stamped.transform.translation.y = 0.0
|
||||
transform_stamped.transform.translation.z = 0.0
|
||||
transform_stamped.transform.rotation.x = 0.0
|
||||
@ -29,28 +27,16 @@ class TF2PublisherNode(Node):
|
||||
transform_stamped.transform.rotation.z = 0.0
|
||||
transform_stamped.transform.rotation.w = 1.0
|
||||
|
||||
|
||||
self.x += 0.01
|
||||
|
||||
self.tf_broadcaster.sendTransform(transform_stamped)
|
||||
|
||||
transform_stamped = gm.TransformStamped()
|
||||
transform_stamped.header.stamp = self.get_clock().now().to_msg()
|
||||
transform_stamped.header.frame_id = 'base_link'
|
||||
transform_stamped.child_frame_id = 'velodyne'
|
||||
|
||||
# Identity transform (no translation, no rotation)
|
||||
transform_stamped.transform.translation.x = 0.0
|
||||
transform_stamped.transform.translation.y = self.x
|
||||
transform_stamped.transform.translation.z = 0.0
|
||||
transform_stamped.transform.rotation.x = 0.0
|
||||
transform_stamped.transform.rotation.y = 0.0
|
||||
transform_stamped.transform.rotation.z = 0.0
|
||||
transform_stamped.transform.rotation.w = 1.0
|
||||
|
||||
self.tf_broadcaster.sendTransform(transform_stamped)
|
||||
|
||||
|
||||
def publish_identity_transform(self):
|
||||
# self.send_parent_child_identity_tf("map", "odom")
|
||||
# self.send_parent_child_identity_tf("odom", "base_link")
|
||||
# self.send_parent_child_identity_tf("base_link", "base_scan")
|
||||
# self.send_parent_child_identity_tf("base_scan", "velodyne")
|
||||
self.send_parent_child_identity_tf("base_link", "velodyne")
|
||||
self.send_parent_child_identity_tf("base_link", "base_footprint")
|
||||
|
||||
|
||||
self.get_logger().info('Published identity transform')
|
||||
|
@ -7,8 +7,8 @@ from sensor_msgs.msg import Joy
|
||||
|
||||
import numpy as np
|
||||
|
||||
SCALE_LINEAR_VELOCITY = 0.1
|
||||
SCALE_ANGULAR_VELOCITY = 0.1
|
||||
SCALE_LINEAR_VELOCITY = 0.2
|
||||
SCALE_ANGULAR_VELOCITY = 0.7
|
||||
PUBLISH_FREQUENCY_HZ = 30
|
||||
|
||||
SMOOTH_WEIGHTS = np.array([1, 2, 4, 10, 20])
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
* @param value_left encoder value left
|
||||
* @param value_right encoder value right
|
||||
*/
|
||||
static void sendBothEncoderValues(float value_left, float value_right);
|
||||
static void sendBothEncoderValues(float speed_right, float speed_left, float pos_right, float pos_left);
|
||||
};
|
||||
|
||||
#endif
|
@ -13,7 +13,6 @@
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "R2WD.h"
|
||||
|
||||
class Utils {
|
||||
private:
|
||||
|
385
src/dcaitirobot/embedded/lib/MotorWheel/MotorWheel.cpp
Normal file
385
src/dcaitirobot/embedded/lib/MotorWheel/MotorWheel.cpp
Normal file
@ -0,0 +1,385 @@
|
||||
#include<MotorWheel.h>
|
||||
|
||||
Motor::Motor(unsigned char _pinPWM,unsigned char _pinDir,
|
||||
unsigned char _pinIRQ,unsigned char _pinIRQB,
|
||||
struct ISRVars* _isr)
|
||||
:PID(&speedRPMInput,&speedRPMOutput,&speedRPMDesired,KC,TAUI,TAUD),
|
||||
pinPWM(_pinPWM),pinDir(_pinDir),isr(_isr) {
|
||||
debug();
|
||||
|
||||
isr->pinIRQ=_pinIRQ;
|
||||
isr->pinIRQB=_pinIRQB;
|
||||
|
||||
/*for maple*/
|
||||
#if defined(BOARD_maple) || defined(BOARD_maple_native) || defined(BOARD_maple_mini)
|
||||
pinMode(pinPWM,PWM);
|
||||
pinMode(pinDir,OUTPUT);
|
||||
pinMode(isr->pinIRQ,INPUT_FLOATING);
|
||||
|
||||
/*for arduino*/
|
||||
#else
|
||||
pinMode(pinPWM,OUTPUT);
|
||||
pinMode(pinDir,OUTPUT);
|
||||
pinMode(isr->pinIRQ,INPUT);
|
||||
|
||||
#endif
|
||||
if(isr->pinIRQB!=PIN_UNDEFINED) {
|
||||
pinMode(isr->pinIRQB,INPUT);
|
||||
}
|
||||
|
||||
PIDDisable();
|
||||
}
|
||||
|
||||
void Motor::setupInterrupt() {
|
||||
/*for maple*/
|
||||
#if defined(BOARD_maple) || defined(BOARD_maple_native) || defined(BOARD_maple_mini)
|
||||
attachInterrupt(isr->pinIRQ,isr->ISRfunc,TRIGGER); // RISING --> CHANGE 201207
|
||||
|
||||
/*for arduino*/
|
||||
#else
|
||||
if(isr->pinIRQ==2 || isr->pinIRQ==3) attachInterrupt(isr->pinIRQ-2,isr->ISRfunc,TRIGGER);
|
||||
else {
|
||||
PCattachInterrupt(isr->pinIRQ,isr->ISRfunc,TRIGGER); // RISING --> CHANGE 201207
|
||||
}
|
||||
//#ifndef _NAMIKI_MOTOR
|
||||
// // Namiki motor can use 2 times only, because of the distance of the two optical sensors
|
||||
// // 4 times of pulses, 201208
|
||||
// if(isr->pinIRQB==2 || isr->pinIRQB==3) attachInterrupt(isr->pinIRQB-2,isr->ISRfunc,CHANGE);
|
||||
// else {
|
||||
// PCattachInterrupt(isr->pinIRQB,isr->ISRfunc,CHANGE); // RISING --> CHANGE 201207
|
||||
// }
|
||||
//#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned char Motor::getPinPWM() const {
|
||||
debug();
|
||||
return pinPWM;
|
||||
}
|
||||
unsigned char Motor::getPinDir() const {
|
||||
debug();
|
||||
return pinDir;
|
||||
}
|
||||
unsigned char Motor::getPinIRQ() const {
|
||||
debug();
|
||||
return isr->pinIRQ;
|
||||
}
|
||||
unsigned char Motor::getPinIRQB() const {
|
||||
debug();
|
||||
return isr->pinIRQB;
|
||||
}
|
||||
|
||||
unsigned int Motor::runPWM(unsigned int PWM,bool dir,bool saveDir) {
|
||||
debug();
|
||||
speedPWM=PWM;
|
||||
if(saveDir) desiredDirection=dir;
|
||||
analogWrite(pinPWM,PWM);
|
||||
digitalWrite(pinDir,dir);
|
||||
return PWM;
|
||||
}
|
||||
unsigned int Motor::advancePWM(unsigned int PWM) {
|
||||
debug();
|
||||
return runPWM(PWM,DIR_ADVANCE);
|
||||
}
|
||||
unsigned int Motor::backoffPWM(unsigned int PWM) {
|
||||
debug();
|
||||
return runPWM(PWM,DIR_BACKOFF);
|
||||
}
|
||||
unsigned int Motor::getPWM() const {
|
||||
debug();
|
||||
return speedPWM;
|
||||
}
|
||||
bool Motor::setDesiredDir(bool dir) {
|
||||
debug();
|
||||
//runPWM(getPWM(),dir); // error
|
||||
desiredDirection=dir;
|
||||
return getDesiredDir();
|
||||
}
|
||||
bool Motor::getDesiredDir() const {
|
||||
debug();
|
||||
return desiredDirection;
|
||||
}
|
||||
bool Motor::reverseDesiredDir() {
|
||||
debug();
|
||||
runPWM(getPWM(),!getDesiredDir());
|
||||
return getDesiredDir();
|
||||
}
|
||||
|
||||
bool Motor::getCurrDir() const {
|
||||
return isr->currDirection;
|
||||
}
|
||||
bool Motor::setCurrDir() {
|
||||
if(getPinIRQB()!=PIN_UNDEFINED)
|
||||
return isr->currDirection=digitalRead(isr->pinIRQB);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
int Motor::getAccRPMM() const {
|
||||
debug();
|
||||
return SPEEDPPS2SPEEDRPM(isr->accPPSS);
|
||||
}
|
||||
*/
|
||||
/*
|
||||
unsigned int Motor::getSpeedRPM() const {
|
||||
debug();
|
||||
return SPEEDPPS2SPEEDRPM(isr->speedPPS);
|
||||
}
|
||||
*/
|
||||
unsigned int Motor::setSpeedRPM(int speedRPM,bool dir) {
|
||||
debug();
|
||||
PIDSetSpeedRPMDesired(speedRPM);
|
||||
setDesiredDir(dir);
|
||||
return abs(getSpeedRPM());
|
||||
}
|
||||
// direction sensitive 201208
|
||||
int Motor::getSpeedRPM() const {
|
||||
debug();
|
||||
if(getCurrDir()==DIR_ADVANCE)
|
||||
return SPEEDPPS2SPEEDRPM(isr->speedPPS);
|
||||
return -SPEEDPPS2SPEEDRPM(isr->speedPPS);
|
||||
}
|
||||
int Motor::setSpeedRPM(int speedRPM) {
|
||||
debug();
|
||||
if(speedRPM>=0) return setSpeedRPM(speedRPM,DIR_ADVANCE);
|
||||
else return setSpeedRPM(abs(speedRPM),DIR_BACKOFF);
|
||||
}
|
||||
|
||||
|
||||
bool Motor::PIDGetStatus() const {
|
||||
debug();
|
||||
return pidCtrl;
|
||||
}
|
||||
bool Motor::PIDEnable(float kc,float taui,float taud,unsigned int sampleTime) {
|
||||
debug();
|
||||
setupInterrupt();
|
||||
PIDSetup(kc,taui,taud,sampleTime);
|
||||
return pidCtrl=true;
|
||||
}
|
||||
bool Motor::PIDDisable() {
|
||||
debug();
|
||||
|
||||
return pidCtrl=false;
|
||||
}
|
||||
bool Motor::PIDReset() {
|
||||
debug();
|
||||
if(PIDGetStatus()==false) return false;
|
||||
PID::Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Motor::PIDSetup(float kc,float taui,float taud,unsigned int sampleTime) {
|
||||
debug();
|
||||
PID::SetTunings(kc,taui,taud);
|
||||
PID::SetInputLimits(0,MAX_SPEEDRPM);
|
||||
PID::SetOutputLimits(0,MAX_SPEEDRPM);
|
||||
PID::SetSampleTime(sampleTime);
|
||||
PID::SetMode(AUTO);
|
||||
return true;
|
||||
}
|
||||
unsigned int Motor::PIDSetSpeedRPMDesired(unsigned int speedRPM) {
|
||||
debug();
|
||||
if(speedRPM>MAX_SPEEDRPM) speedRPMDesired=MAX_SPEEDRPM;
|
||||
else speedRPMDesired=speedRPM;
|
||||
return PIDGetSpeedRPMDesired();
|
||||
}
|
||||
unsigned int Motor::PIDGetSpeedRPMDesired() const {
|
||||
debug();
|
||||
return speedRPMDesired;
|
||||
}
|
||||
|
||||
bool Motor::PIDRegulate(bool doRegulate) {
|
||||
debug();
|
||||
if(PIDGetStatus()==false) return false;
|
||||
if(getPinIRQB()!=PIN_UNDEFINED && getDesiredDir()!=getCurrDir()) {
|
||||
speedRPMInput=-SPEEDPPS2SPEEDRPM(isr->speedPPS);
|
||||
} else {
|
||||
speedRPMInput=SPEEDPPS2SPEEDRPM(isr->speedPPS);
|
||||
}
|
||||
|
||||
PID::Compute();
|
||||
if(doRegulate && PID::JustCalculated()) {
|
||||
speed2DutyCycle+=speedRPMOutput;
|
||||
|
||||
if(speed2DutyCycle>=MAX_SPEEDRPM) speed2DutyCycle=MAX_SPEEDRPM;
|
||||
else if(speed2DutyCycle<=-MAX_SPEEDRPM) speed2DutyCycle=-MAX_SPEEDRPM;
|
||||
if(speed2DutyCycle>=0) {
|
||||
runPWM(map(speed2DutyCycle,0,MAX_SPEEDRPM,0,MAX_PWM),getDesiredDir(),false);
|
||||
} else {
|
||||
runPWM(map(abs(speed2DutyCycle),0,MAX_SPEEDRPM,0,MAX_PWM),!getDesiredDir(),false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Motor::getSpeedPPS() const {
|
||||
return isr->speedPPS;
|
||||
}
|
||||
long Motor::getCurrPulse() const {
|
||||
return isr->pulses;
|
||||
}
|
||||
long Motor::setCurrPulse(long _pulse) {
|
||||
isr->pulses=_pulse;
|
||||
return getCurrPulse();
|
||||
}
|
||||
long Motor::resetCurrPulse() {
|
||||
return setCurrPulse(0);
|
||||
}
|
||||
|
||||
void Motor::delayMS(unsigned int ms,bool debug) {
|
||||
for(unsigned long endTime=millis()+ms;millis()<endTime;) {
|
||||
PIDRegulate();
|
||||
if(debug && (millis()%500==0)) debugger();
|
||||
if(endTime-millis()>=SAMPLETIME) delay(SAMPLETIME);
|
||||
else delay(endTime-millis());
|
||||
}
|
||||
}
|
||||
|
||||
// to reduce the binary for Atmega168 (16KB), Serial output is disabled
|
||||
void Motor::debugger() const {
|
||||
debug()
|
||||
|
||||
#ifdef DEBUG
|
||||
if(!Serial.available()) Serial.begin(Baudrate);
|
||||
/*
|
||||
Serial.print("pinPWM -> ");
|
||||
Serial.println(pinPWM,DEC);
|
||||
Serial.print("pinDir -> ");
|
||||
Serial.println(pinDir,DEC);
|
||||
Serial.print("pinIRQ -> ");
|
||||
Serial.println(pinIRQ,DEC);
|
||||
Serial.print("pinIRQB-> ");
|
||||
Serial.println(pinIRQB,DEC);
|
||||
*/
|
||||
|
||||
Serial.print("DesiredDir -> ");
|
||||
Serial.println(desiredDirection);
|
||||
Serial.print("currDir ->");
|
||||
Serial.println(isr->currDirection);
|
||||
|
||||
Serial.print("PWM -> ");
|
||||
Serial.println(speedPWM,DEC);
|
||||
Serial.print("Input -> ");
|
||||
Serial.println(speedRPMInput,DEC);
|
||||
Serial.print("Output -> ");
|
||||
Serial.println(speedRPMOutput,DEC);
|
||||
Serial.print("Desired-> ");
|
||||
Serial.println(speedRPMDesired,DEC);
|
||||
|
||||
/*
|
||||
Serial.print("speed2DutyCycle-> ");
|
||||
Serial.println(speed2DutyCycle);
|
||||
Serial.print("speedPPS> ");
|
||||
Serial.println(isr->speedPPS,DEC);
|
||||
Serial.print("pulses -> ");
|
||||
Serial.println(isr->pulses,DEC);
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
GearedMotor::GearedMotor(unsigned char _pinPWM,unsigned char _pinDir,
|
||||
unsigned char _pinIRQ,unsigned char _pinIRQB,
|
||||
struct ISRVars* _isr,unsigned int ratio):
|
||||
Motor(_pinPWM,_pinDir,_pinIRQ,_pinIRQB,_isr),_ratio(ratio) {
|
||||
debug();
|
||||
}
|
||||
unsigned int GearedMotor::getRatio() const {
|
||||
return _ratio;
|
||||
}
|
||||
unsigned int GearedMotor::setRatio(unsigned int ratio) {
|
||||
_ratio=ratio;
|
||||
return getRatio();
|
||||
}
|
||||
/*
|
||||
float GearedMotor::getGearedAccRPMM() const {
|
||||
debug();
|
||||
return (float)Motor::getAccRPMM()/getRatio();
|
||||
}
|
||||
*/
|
||||
float GearedMotor::getGearedSpeedRPM() const {
|
||||
debug();
|
||||
//return (float)Motor::getSpeedRPM()/REDUCTION_RATIO;
|
||||
//return (float)Motor::getSpeedRPM()/_ratio;
|
||||
return (float)Motor::getSpeedRPM()/getRatio();
|
||||
}
|
||||
float GearedMotor::setGearedSpeedRPM(float gearedSpeedRPM,bool dir) {
|
||||
debug();
|
||||
//Motor::setSpeedRPM(abs(gearedSpeedRPM*REDUCTION_RATIO),dir);
|
||||
Motor::setSpeedRPM(abs(round(gearedSpeedRPM*_ratio)),dir);
|
||||
return getGearedSpeedRPM();
|
||||
}
|
||||
// direction sensitive, 201208
|
||||
float GearedMotor::setGearedSpeedRPM(float gearedSpeedRPM) {
|
||||
debug();
|
||||
//Motor::setSpeedRPM(gearedSpeedRPM*_ratio);
|
||||
Motor::setSpeedRPM(round(gearedSpeedRPM*_ratio));
|
||||
return getGearedSpeedRPM();
|
||||
}
|
||||
|
||||
float GearedMotor::getPosition() {
|
||||
debug();
|
||||
int ticks_per_rev = getRatio() * CPR;
|
||||
return (float)(Motor::getCurrPulse() % ticks_per_rev)/ticks_per_rev;
|
||||
}
|
||||
|
||||
MotorWheel::MotorWheel(unsigned char _pinPWM,unsigned char _pinDir,
|
||||
unsigned char _pinIRQ,unsigned char _pinIRQB,
|
||||
struct ISRVars* _isr,
|
||||
unsigned int ratio,unsigned int cirMM):
|
||||
GearedMotor(_pinPWM,_pinDir,_pinIRQ,_pinIRQB,_isr,ratio),_cirMM(cirMM) {
|
||||
debug();
|
||||
|
||||
}
|
||||
unsigned int MotorWheel::getCirMM() const {
|
||||
return _cirMM;
|
||||
}
|
||||
unsigned int MotorWheel::setCirMM(unsigned int cirMM) {
|
||||
if(cirMM>0) _cirMM=cirMM;
|
||||
return getCirMM();
|
||||
}
|
||||
// direction sensitive, 201208
|
||||
int MotorWheel::getSpeedCMPM() const {
|
||||
debug();
|
||||
//return int(GearedMotor::getGearedSpeedRPM()*CIR);
|
||||
return int(GearedMotor::getGearedSpeedRPM()*getCirMM()/10);
|
||||
}
|
||||
int MotorWheel::setSpeedCMPM(unsigned int cm,bool dir) {
|
||||
debug();
|
||||
//GearedMotor::setGearedSpeedRPM(cm/CIR,dir);
|
||||
GearedMotor::setGearedSpeedRPM(cm*10.0/getCirMM(),dir);
|
||||
return getSpeedCMPM();
|
||||
}
|
||||
// direction sensitive, 201208
|
||||
int MotorWheel::setSpeedCMPM(int cm) {
|
||||
debug();
|
||||
//GearedMotor::setGearedSpeedRPM(cm/CIR,dir);
|
||||
GearedMotor::setGearedSpeedRPM(cm*10.0/getCirMM());
|
||||
return getSpeedCMPM();
|
||||
}
|
||||
|
||||
void MotorWheel::setGearedRPM(float rpm, bool dir) {
|
||||
debug();
|
||||
GearedMotor::setGearedSpeedRPM(rpm, dir);
|
||||
return ;
|
||||
}
|
||||
// direction sensitive, 201208
|
||||
int MotorWheel::getSpeedMMPS() const {
|
||||
debug();
|
||||
return int(getSpeedCMPM()/6);//(mm/sec)/(cm/min) = 6
|
||||
}
|
||||
|
||||
int MotorWheel::setSpeedMMPS(unsigned int mm,bool dir) {
|
||||
debug();
|
||||
setSpeedCMPM(mm*6,dir);
|
||||
return getSpeedMMPS();
|
||||
}
|
||||
// direction sensitive, 201208
|
||||
int MotorWheel::setSpeedMMPS(int mm) {
|
||||
debug();
|
||||
setSpeedCMPM(mm*6);
|
||||
return getSpeedMMPS();
|
||||
}
|
349
src/dcaitirobot/embedded/lib/MotorWheel/MotorWheel.h
Normal file
349
src/dcaitirobot/embedded/lib/MotorWheel/MotorWheel.h
Normal file
@ -0,0 +1,349 @@
|
||||
|
||||
/*
|
||||
V1.1 201104
|
||||
1. motorwheel library version 1.1,compatible with maple.
|
||||
|
||||
V1.1.1 201110
|
||||
1. Add Acceleration
|
||||
|
||||
V1.2 201207
|
||||
1. Double CPR from 12 to 24, Interrupt Type from RISING to CHANGE.
|
||||
2. Reduce default sample time from 10ms to 5ms.
|
||||
3. Compatible with Namiki 22CL-3501PG80:1, by "#define _NAMIKI_MOTOR" before "#include ...".
|
||||
|
||||
V1.3 201209
|
||||
1. R2WD::delayMS(), Omni3WD::delayMS(), Omni4WD::delayMS() are re-implemented, more exactly.
|
||||
2. getSpeedRPM(), setSpeedRPM(), getSpeedMMPS(), setSpeedMMPS() are plug-minus/direction sensitive now.
|
||||
3. Acceleration is disabled.
|
||||
|
||||
V1.4 201209 Not Released
|
||||
1. Increase CPR from 24 to 48 for Faulhaber 2342.
|
||||
|
||||
V1.5 201209 Omni4WD is re-implemented, and now return value of Omni4WD::getSpeedMMPS() is correct.
|
||||
|
||||
*/
|
||||
|
||||
/*for maple*/
|
||||
#if defined(BOARD_maple) || defined(BOARD_maple_native) || defined(BOARD_maple_mini)
|
||||
#include "wirish.h"
|
||||
#include "./../PID_Beta6/PID_Beta6.h"
|
||||
#include "ext_interrupts.h"
|
||||
|
||||
/*for arduino*/
|
||||
#else
|
||||
//#include<Arduino.h>
|
||||
//#include<WProgram.h>
|
||||
#include<PID_Beta6.h>
|
||||
#include<PinChangeInt.h>
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
class PID
|
||||
class Motor
|
||||
class GearedMotor
|
||||
class MotorWheel
|
||||
|
||||
*/
|
||||
|
||||
#ifndef MotorWheel_H
|
||||
#define MotorWheel_H
|
||||
|
||||
#define DIR_ADVANCE HIGH
|
||||
#define DIR_BACKOFF LOW
|
||||
|
||||
#define PIN_UNDEFINED 255
|
||||
#define REF_VOLT 12
|
||||
/*for maple*/
|
||||
#if defined(BOARD_maple) || defined(BOARD_maple_native) || defined(BOARD_maple_mini)
|
||||
#define MAX_PWM 3599
|
||||
|
||||
/*for arduino*/
|
||||
#else
|
||||
#define MAX_PWM 255
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _NAMIKI_MOTOR
|
||||
#define TRIGGER CHANGE
|
||||
#define CPR 4 // Namiki motor
|
||||
#define DIR_INVERSE
|
||||
#define REDUCTION_RATIO 80
|
||||
#else
|
||||
#define TRIGGER CHANGE
|
||||
#define CPR 24 // Faulhaber motor
|
||||
#define DIR_INVERSE !
|
||||
#define REDUCTION_RATIO 64
|
||||
#endif
|
||||
|
||||
#define MAX_SPEEDRPM 8000
|
||||
//#define DESIRED_SPEEDRPM 3000
|
||||
//#define SPEED_CONSTANT_RPM 713
|
||||
//#define PWM2SPEED_RESOLUTION 143 //713 * 0.2
|
||||
|
||||
#define SEC_PER_MIN 60
|
||||
#define MICROS_PER_SEC 1000000
|
||||
//#define SPEEDPPS2SPEEDRPM(freq) ((freq)*((SEC_PER_MIN)/(CPR))) //(freq*SEC_PER_MIN/CPR)
|
||||
#define SPEEDPPS2SPEEDRPM(freq) ((unsigned long)(freq)*(SEC_PER_MIN)/(CPR)) //(freq*SEC_PER_MIN/CPR)
|
||||
//#define SPEEDPPS2SPEED2VOLT(spd) ((spd)/SPEED_CONSTANT_RPM)
|
||||
//#define MAX_SPEEDPPS ((MAX_SPEEDRPM*CPR)/SEC_PER_MIN)
|
||||
//#define MIN_SPEEDPPS 0
|
||||
|
||||
#define KC 0.31
|
||||
#define TAUI 0.02
|
||||
#define TAUD 0.00
|
||||
#define SAMPLETIME 5
|
||||
|
||||
#define Baudrate 19200
|
||||
/*
|
||||
#ifndef DEBUG
|
||||
#define DEBUG
|
||||
#endif
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
#define debug() { \
|
||||
if(!Serial.available()) Serial.begin(Baudrate); \
|
||||
Serial.println(__func__);}
|
||||
#else
|
||||
#define debug() {}
|
||||
#endif
|
||||
|
||||
|
||||
// Interrupt Type: RISING --> CHANGE 201207
|
||||
#define irqISR(y,x) \
|
||||
void x(); \
|
||||
struct ISRVars y={x}; \
|
||||
void x() { \
|
||||
static bool first_pulse=true; \
|
||||
y.pulseEndMicros=micros(); \
|
||||
if(first_pulse==false && y.pulseEndMicros>y.pulseStartMicros) { \
|
||||
y.speedPPS=MICROS_PER_SEC/(y.pulseEndMicros-y.pulseStartMicros); \
|
||||
/* y.accPPSS=(y.speedPPS-y.lastSpeedPPS)*y.speedPPS; */ \
|
||||
} else first_pulse=false; \
|
||||
y.pulseStartMicros=y.pulseEndMicros; \
|
||||
/* y.lastSpeedPPS=y.speedPPS; */ \
|
||||
if(y.pinIRQB!=PIN_UNDEFINED) \
|
||||
y.currDirection=DIR_INVERSE(digitalRead(y.pinIRQ)^digitalRead(y.pinIRQB)); \
|
||||
y.currDirection==DIR_ADVANCE?++y.pulses:--y.pulses; \
|
||||
}
|
||||
|
||||
struct ISRVars {
|
||||
void (*ISRfunc)();
|
||||
//volatile unsigned long pulses;
|
||||
volatile long pulses; // 201104, direction sensitive
|
||||
volatile unsigned long pulseStartMicros;
|
||||
volatile unsigned long pulseEndMicros;
|
||||
volatile unsigned int speedPPS;
|
||||
//volatile unsigned int lastSpeedPPS;
|
||||
//volatile int accPPSS; // acceleration, Pulse Per Sec^2
|
||||
volatile bool currDirection;
|
||||
unsigned char pinIRQB;
|
||||
unsigned char pinIRQ; // pinIRQA 201207
|
||||
};
|
||||
|
||||
class Motor: public PID {
|
||||
public:
|
||||
Motor(unsigned char _pinPWM,unsigned char _pinDir,
|
||||
unsigned char _pinIRQ,unsigned char _pinIRQB,
|
||||
struct ISRVars* _isr);
|
||||
|
||||
void setupInterrupt();
|
||||
|
||||
unsigned char getPinPWM() const;
|
||||
unsigned char getPinDir() const;
|
||||
unsigned char getPinIRQ() const;
|
||||
unsigned char getPinIRQB() const;
|
||||
|
||||
unsigned int runPWM(unsigned int PWM,bool dir,bool saveDir=true);
|
||||
unsigned int getPWM() const;
|
||||
unsigned int advancePWM(unsigned int PWM);
|
||||
unsigned int backoffPWM(unsigned int PWM);
|
||||
|
||||
bool setDesiredDir(bool dir);
|
||||
bool getDesiredDir() const;
|
||||
bool reverseDesiredDir();
|
||||
|
||||
bool setCurrDir();
|
||||
bool getCurrDir() const;
|
||||
|
||||
//int getAccRPMM() const; // Acceleration, Round Per Min^2
|
||||
//unsigned int getSpeedRPM() const;
|
||||
//unsigned int setSpeedRPM(int speedRPM,bool dir);
|
||||
// direction sensitive 201208
|
||||
int getSpeedRPM() const;
|
||||
unsigned int setSpeedRPM(int speedRPM,bool dir); // preserve
|
||||
int setSpeedRPM(int speedRPM);
|
||||
|
||||
//void simpleRegulate();
|
||||
|
||||
bool PIDSetup(float kc=KC,float taui=TAUI,float taud=TAUD,unsigned int sampleTime=1000);
|
||||
bool PIDGetStatus() const;
|
||||
bool PIDEnable(float kc=KC,float taui=TAUI,float taud=TAUD,unsigned int sampleTime=1000);
|
||||
bool PIDDisable();
|
||||
bool PIDReset();
|
||||
bool PIDRegulate(bool doRegulate=true);
|
||||
unsigned int PIDSetSpeedRPMDesired(unsigned int speedRPM);
|
||||
unsigned int PIDGetSpeedRPMDesired() const;
|
||||
|
||||
void delayMS(unsigned int ms,bool debug=false);
|
||||
void debugger() const;
|
||||
|
||||
//int getAccPPSS() const;
|
||||
int getSpeedPPS() const;
|
||||
long getCurrPulse() const;
|
||||
long setCurrPulse(long _pulse);
|
||||
long resetCurrPulse();
|
||||
|
||||
struct ISRVars* isr;
|
||||
|
||||
private:
|
||||
unsigned char pinPWM;
|
||||
unsigned char pinDir;
|
||||
//unsigned char pinIRQ; // moved to isr
|
||||
//unsigned char pinIRQB;
|
||||
|
||||
//bool currDirection; // current direction
|
||||
bool desiredDirection; // desired direction
|
||||
unsigned int speedPWM; // current PWM
|
||||
|
||||
int speedRPMInput; // RPM: Round Per Minute
|
||||
int speedRPMOutput; // RPM
|
||||
int speedRPMDesired; // RPM
|
||||
//float PWMEC;
|
||||
float speed2DutyCycle;
|
||||
/*
|
||||
// the followings are defined in struct ISRvars,
|
||||
// because ISR must be a global function, without parameters and no return value
|
||||
|
||||
volatile unsigned int speedPPS; // PPS: Pulses Per Second
|
||||
volatile unsigned long pulseStartMicros;
|
||||
volatile unsigned long pulseEndMicros;
|
||||
*/
|
||||
bool pidCtrl;
|
||||
|
||||
Motor();
|
||||
|
||||
};
|
||||
|
||||
|
||||
#ifndef REDUCTION_RATIO
|
||||
#define REDUCTION_RATIO 64
|
||||
#endif
|
||||
class GearedMotor: public Motor { // RPM
|
||||
public:
|
||||
GearedMotor(unsigned char _pinPWM,unsigned char _pinDir,
|
||||
unsigned char _pinIRQ,unsigned char _pinIRQB,
|
||||
struct ISRVars* _isr,
|
||||
unsigned int _ratio=REDUCTION_RATIO);
|
||||
//float getGearedAccRPMM() const; // Acceleration, Round Per Min^2
|
||||
float getGearedSpeedRPM() const;
|
||||
float setGearedSpeedRPM(float gearedSpeedRPM,bool dir);
|
||||
// direction sensitive 201208
|
||||
float setGearedSpeedRPM(float gearedSpeedRPM);
|
||||
unsigned int getRatio() const;
|
||||
unsigned int setRatio(unsigned int ratio=REDUCTION_RATIO);
|
||||
float getPosition();
|
||||
private:
|
||||
unsigned int _ratio;
|
||||
};
|
||||
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.1416
|
||||
#endif
|
||||
//#define CIR 31.4 // cm
|
||||
//#define CIRMM 150 // mm
|
||||
//#define CIRMM 188
|
||||
#define CIRMM 314
|
||||
class MotorWheel: public GearedMotor { //
|
||||
public:
|
||||
MotorWheel(unsigned char _pinPWM,unsigned char _pinDir,
|
||||
unsigned char _pinIRQ,unsigned char _pinIRQB,
|
||||
struct ISRVars* _isr,
|
||||
unsigned int ratio=REDUCTION_RATIO,unsigned int cirMM=CIRMM);
|
||||
|
||||
unsigned int getCirMM() const;
|
||||
unsigned int setCirMM(unsigned int cirMM=CIRMM);
|
||||
/*
|
||||
int getAccCMPMM() const; // Acceleration, CM Per Min^2
|
||||
unsigned int getSpeedCMPM() const; // cm/min
|
||||
unsigned int setSpeedCMPM(unsigned int cm,bool dir);
|
||||
int getAccMMPSS() const; // Acceleration, MM Per Sec^2
|
||||
unsigned int getSpeedMMPS() const; // mm/s
|
||||
unsigned int setSpeedMMPS(unsigned int mm,bool dir);
|
||||
*/
|
||||
// direction sensitive 201208
|
||||
//int getAccCMPMM() const; // Acceleration, CM Per Min^2
|
||||
int getSpeedCMPM() const; // cm/min
|
||||
int setSpeedCMPM(unsigned int cm,bool dir); // preserve
|
||||
int setSpeedCMPM(int cm);
|
||||
//int getAccMMPSS() const; // Acceleration, MM Per Sec^2
|
||||
int getSpeedMMPS() const; // mm/s
|
||||
int setSpeedMMPS(unsigned int mm,bool dir); // preserve
|
||||
int setSpeedMMPS(int mm);
|
||||
void setGearedRPM(float rpm, bool dir);
|
||||
|
||||
//unsigned int runTime(unsigned int speedMMPS,bool dir,unsigned int TimeMS);
|
||||
//unsigned int runDistance(unsigned int speedMMPS,bool dir,unsigned int distanceMM);
|
||||
|
||||
private:
|
||||
unsigned int _cirMM;
|
||||
};
|
||||
|
||||
/*
|
||||
class samePID: public PID {
|
||||
public:
|
||||
samePID(int* input,int* output,int* setPoint,float kc,float taui,float taud)
|
||||
:PID(input,output,setPoint,kc,taui,taud) { }
|
||||
};
|
||||
|
||||
class servoMotor: public Motor,public samePID {
|
||||
public:
|
||||
servoMotor(unsigned char _pinPWM,unsigned char _pinDir,
|
||||
unsigned char _pinIRQ,unsigned char _pinIRQB,
|
||||
struct ISRVars* _isr);
|
||||
|
||||
int SPIDSetPulseBoundary(int min,int max);
|
||||
bool SPIDSetup(float kc=KC,float taui=TAUI,float taud=TAUD,unsigned int sampleTime=10);
|
||||
bool PIDRegulate(bool doRegulate=true);
|
||||
|
||||
enum {
|
||||
SPIDMODE_UNKNOWN,
|
||||
SPIDMODE_SPEED,
|
||||
SPIDMODE_STOP,
|
||||
SPIDMODE_PULSE,
|
||||
};
|
||||
unsigned char getSPIDMode() const;
|
||||
unsigned char setSPIDMode(unsigned char _mode);
|
||||
|
||||
int getPulseDesired() const;
|
||||
int setPulseDesired(int _pulse);
|
||||
int incPulseDesired();
|
||||
int decPulseDesired();
|
||||
|
||||
bool backToZero();
|
||||
bool scanZero();
|
||||
|
||||
void debugger() const;
|
||||
|
||||
private:
|
||||
int pulseInput; //
|
||||
int pulseOutput; //
|
||||
int pulseDesired; //
|
||||
|
||||
int pulse2SpeedRPM;
|
||||
|
||||
unsigned char SPIDMode;
|
||||
|
||||
bool SPIDRegulateSpeed(bool doRegulate=true); // 201104
|
||||
bool SPIDRegulateStop(bool doRegulate=true);
|
||||
bool SPIDRegulatePulse(bool doRegulate=true);
|
||||
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,31 @@
|
||||
/********************************************************
|
||||
* PID Simple Example
|
||||
* Reading analog input 0 to control analog PWM output 3
|
||||
********************************************************/
|
||||
|
||||
#include <PID_Beta6.h>
|
||||
|
||||
//Define Variables we'll be connecting to
|
||||
double Setpoint, Input, Output;
|
||||
|
||||
//Specify the links and initial tuning parameters
|
||||
PID myPID(&Input, &Output, &Setpoint,2,5,1);
|
||||
|
||||
void setup()
|
||||
{
|
||||
//initialize the variables we're linked to
|
||||
Input = analogRead(0);
|
||||
Setpoint = 100;
|
||||
|
||||
//turn the PID on
|
||||
myPID.SetMode(AUTO);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
Input = analogRead(0);
|
||||
myPID.Compute();
|
||||
analogWrite(3,Output);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,55 @@
|
||||
/***************************************************
|
||||
* Advanced PID Example
|
||||
* PID input: Analog input 0
|
||||
* output = time proportion on/off of output 0 (50% = 2 sec on, 2 sec off)
|
||||
* and just to make it more complicated:
|
||||
* - start with a setpoint ramp (0 to 500 over 5 minutes (300 seconds))
|
||||
* - use gap control (moderate tunings when within 100 of setpoint, aggressive when outside 100)
|
||||
***************************************************/
|
||||
|
||||
#include <PID_Beta6.h>
|
||||
|
||||
unsigned long windowStartTime;
|
||||
unsigned long RampStartTime;
|
||||
double Input, Output, Setpoint;
|
||||
PID pid(&Input, &Output, &Setpoint, 3,4,1);
|
||||
|
||||
unsigned long printTime= millis();
|
||||
|
||||
void setup()
|
||||
{
|
||||
|
||||
pid.SetOutputLimits(0,4000); //tell the PID to range the output from 0 to 4000
|
||||
Output = 4000; //start the output at its max and let the PID adjust it from there
|
||||
|
||||
pid.SetMode(AUTO); //turn on the PID
|
||||
windowStartTime = millis();
|
||||
RampStartTime = millis();
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
//Set Point Ramp
|
||||
if(millis()-RampStartTime<300000)
|
||||
{
|
||||
Setpoint = ((double)(millis()-RampStartTime))/(300000.0)*500.0;
|
||||
}
|
||||
else Setpoint = 500;
|
||||
|
||||
//Set Tuning Parameters based on how close we are to setpoint
|
||||
if(abs(Setpoint-Input)>100)pid.SetTunings(6,4,1); //aggressive
|
||||
else pid.SetTunings(3,4,1); //comparatively moderate
|
||||
|
||||
//give the PID the opportunity to compute if needed
|
||||
Input = analogRead(0);
|
||||
pid.Compute();
|
||||
|
||||
//turn the output pin on/off based on pid output
|
||||
if(millis()-windowStartTime>4000) windowStartTime+=4000;
|
||||
if(Output > millis()-windowStartTime) digitalWrite(0,HIGH);
|
||||
else digitalWrite(0,LOW);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
#include <fuzzy_table.h>
|
||||
#include <PID_Beta6.h>
|
||||
|
||||
/***************************************************
|
||||
* Feed Forward Example : RepRap Extruder Nozzle Temperature
|
||||
* PID input: Nozzle Temperature
|
||||
* PID output: PWM signal (0-255) to the heater control
|
||||
* Other Input: Plastic Feed rate is being read on input 1
|
||||
* For the feed forward piece...
|
||||
* The amount of heat we want to add to the nozzle is largely dependent
|
||||
* on how fast plastic is being fed. if the feed is stopped we're going
|
||||
* to want a lot less heat than when the system is running. A pid
|
||||
* control by itself will eventually adjust the heat, but by then the temp
|
||||
* will be really high. feed forward can help with this.
|
||||
* so to make Feed Forward work, at every cycle we look at the current feed speed
|
||||
* and compute a "bias" which we feed to the pid controller. sort of: "Based on my
|
||||
* knowledge, your output should be about X. adjust off of that as you see fit"
|
||||
* What does it get you? well, the instant the feed stops, the baseline drops. That
|
||||
* means that the controller output immediately drops. if you had been using just a pid,
|
||||
* you'd have to wait for the temperature to rise quite a bit before seeing the same
|
||||
* drop in output.
|
||||
*
|
||||
* (for this example, we're assuming that for a feed rate of 1023 the output should be
|
||||
* about 130, and at a rate of 0 the output should be around 10)
|
||||
***************************************************/
|
||||
|
||||
#include <PID_Beta6.h>
|
||||
int FeedRate;
|
||||
|
||||
double Input, Output, Setpoint, Bias;
|
||||
PID pid(&Input, &Output, &Setpoint, &Bias, 3, 4, 1);
|
||||
|
||||
void setup()
|
||||
{
|
||||
Setpoint = 400;
|
||||
pid.SetMode(AUTO); //turn on the PID
|
||||
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
//Read in the Feed rate and compute a baseline output to the heater
|
||||
FeedRate = analogRead(1); //read the feed rate
|
||||
Bias = (FeedRate/1023)*(120)+10;
|
||||
|
||||
//give the PID the opportunity to compute if needed
|
||||
Input = analogRead(0);
|
||||
pid.Compute();
|
||||
|
||||
analogWrite(0, Output);
|
||||
|
||||
}
|
309
src/dcaitirobot/embedded/lib/PID_Beta6/PID_Beta6.cpp
Normal file
309
src/dcaitirobot/embedded/lib/PID_Beta6/PID_Beta6.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "wiring.h"
|
||||
#endif
|
||||
|
||||
#include <PID_Beta6.h>
|
||||
#include "fuzzy_table.h"
|
||||
#include <wiring_private.h>
|
||||
#include <HardwareSerial.h>
|
||||
|
||||
/* Standard Constructor (...)***********************************************
|
||||
* constructor used by most users. the parameters specified are those for
|
||||
* for which we can't set up reliable defaults, so we need to have the user
|
||||
* set them.
|
||||
***************************************************************************/
|
||||
PID::PID(int *Input, int *Output, int *Setpoint, float Kc, float TauI, float TauD)
|
||||
{
|
||||
|
||||
PID::ConstructorCommon(Input, Output, Setpoint, Kc, TauI, TauD);
|
||||
UsingFeedForward = false;
|
||||
PID::Reset();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* Overloaded Constructor(...)**********************************************
|
||||
* This one is for more advanced users. it's essentially the same as the
|
||||
* standard constructor, with one addition. you can link to a Feed Forward bias,
|
||||
* which lets you implement... um.. Feed Forward Control. good stuff.
|
||||
***************************************************************************/
|
||||
PID::PID(int *Input, int *Output, int *Setpoint, int *FFBias, float Kc, float TauI, float TauD)
|
||||
{
|
||||
|
||||
PID::ConstructorCommon(Input, Output, Setpoint, Kc, TauI, TauD);
|
||||
UsingFeedForward = true; //tell the controller that we'll be using an external
|
||||
myBias = FFBias; //bias, and where to find it
|
||||
PID::Reset();
|
||||
|
||||
}
|
||||
|
||||
/* ConstructorCommon(...)****************************************************
|
||||
* Most of what is done in the two constructors is the same. that code
|
||||
* was put here for ease of maintenance and (minor) reduction of library size
|
||||
****************************************************************************/
|
||||
void PID::ConstructorCommon(int *Input, int *Output, int *Setpoint, float Kc, float TauI, float TauD)
|
||||
{
|
||||
PID::SetInputLimits(0, 1023); //default the limits to the
|
||||
PID::SetOutputLimits(0, 255); //full ranges of the I/O
|
||||
|
||||
tSample = 1000; //default Controller Sample Time is 1 second
|
||||
|
||||
PID::SetTunings( Kc, TauI, TauD);
|
||||
|
||||
nextCompTime = millis();
|
||||
inAuto = false;
|
||||
myOutput = Output;
|
||||
myInput = Input;
|
||||
mySetpoint = Setpoint;
|
||||
|
||||
Err = lastErr = prevErr = 0;
|
||||
}
|
||||
|
||||
|
||||
/* SetInputLimits(...)*****************************************************
|
||||
* I don't see this function being called all that much (other than from the
|
||||
* constructor.) it needs to be here so we can tell the controller what it's
|
||||
* input limits are, and in most cases the 0-1023 default should be fine. if
|
||||
* there's an application where the signal being fed to the controller is
|
||||
* outside that range, well, then this function's here for you.
|
||||
**************************************************************************/
|
||||
void PID::SetInputLimits(int INMin, int INMax)
|
||||
{
|
||||
//after verifying that mins are smaller than maxes, set the values
|
||||
if(INMin >= INMax) return;
|
||||
|
||||
|
||||
inMin = INMin;
|
||||
inSpan = INMax - INMin;
|
||||
}
|
||||
|
||||
/* SetOutputLimits(...)****************************************************
|
||||
* This function will be used far more often than SetInputLimits. while
|
||||
* the input to the controller will generally be in the 0-1023 range (which is
|
||||
* the default already,) the output will be a little different. maybe they'll
|
||||
* be doing a time window and will need 0-8000 or something. or maybe they'll
|
||||
* want to clamp it from 0-125. who knows. at any rate, that can all be done
|
||||
* here.
|
||||
**************************************************************************/
|
||||
void PID::SetOutputLimits(int OUTMin, int OUTMax)
|
||||
{
|
||||
//after verifying that mins are smaller than maxes, set the values
|
||||
if(OUTMin >= OUTMax) return;
|
||||
|
||||
outMin = OUTMin;
|
||||
outSpan = OUTMax - OUTMin;
|
||||
}
|
||||
|
||||
/* SetTunings(...)*************************************************************
|
||||
* This function allows the controller's dynamic performance to be adjusted.
|
||||
* it's called automatically from the constructor, but tunings can also
|
||||
* be adjusted on the fly during normal operation
|
||||
******************************************************************************/
|
||||
void PID::SetTunings(float Kc, float TauI, float TauD)
|
||||
{
|
||||
//verify that the tunings make sense
|
||||
if (Kc == 0.0 || TauI < 0.0 || TauD < 0.0) return;
|
||||
|
||||
//we're going to do some funky things to the input numbers so all
|
||||
//our math works out, but we want to store the numbers intact
|
||||
//so we can return them to the user when asked.
|
||||
P_Param = Kc;
|
||||
I_Param = TauI;
|
||||
D_Param = TauD;
|
||||
|
||||
//convert Reset Time into Reset Rate, and compensate for Calculation frequency
|
||||
float tSampleInSec = ((float)tSample / 1000.0);
|
||||
float tempTauR;
|
||||
if (TauI == 0.0)
|
||||
tempTauR = 0.0;
|
||||
else
|
||||
tempTauR = (1.0 / TauI) * tSampleInSec;
|
||||
|
||||
|
||||
kc = Kc;
|
||||
taur = tempTauR;
|
||||
taud = TauD / tSampleInSec;
|
||||
|
||||
cof_A = kc * (1 + taur + taud);
|
||||
cof_B = kc * (1 + 2 * taud);
|
||||
cof_C = kc * taud;
|
||||
}
|
||||
|
||||
/* Reset()*********************************************************************
|
||||
* does all the things that need to happen to ensure a bumpless transfer
|
||||
* from manual to automatic mode. this shouldn't have to be called from the
|
||||
* outside. In practice though, it is sometimes helpful to start from scratch,
|
||||
* so it was made publicly available
|
||||
******************************************************************************/
|
||||
void PID::Reset()
|
||||
{
|
||||
|
||||
if(UsingFeedForward)
|
||||
bias = (*myBias - outMin) / outSpan;
|
||||
else
|
||||
bias = (*myOutput - outMin) / outSpan;
|
||||
|
||||
}
|
||||
|
||||
/* SetMode(...)****************************************************************
|
||||
* Allows the controller Mode to be set to manual (0) or Automatic (non-zero)
|
||||
* when the transition from manual to auto occurs, the controller is
|
||||
* automatically initialized
|
||||
******************************************************************************/
|
||||
void PID::SetMode(int Mode)
|
||||
{
|
||||
if (Mode!=0 && !inAuto)
|
||||
{ //we were in manual, and we just got set to auto.
|
||||
//reset the controller internals
|
||||
PID::Reset();
|
||||
}
|
||||
inAuto = (Mode!=0);
|
||||
}
|
||||
|
||||
/* SetSampleTime(...)*******************************************************
|
||||
* sets the frequency, in Milliseconds, with which the PID calculation is performed
|
||||
******************************************************************************/
|
||||
void PID::SetSampleTime(int NewSampleTime)
|
||||
{
|
||||
if (NewSampleTime > 0)
|
||||
{
|
||||
//convert the time-based tunings to reflect this change
|
||||
taur *= ((float)NewSampleTime)/((float) tSample);
|
||||
taud *= ((float)NewSampleTime)/((float) tSample);
|
||||
tSample = (unsigned long)NewSampleTime;
|
||||
|
||||
cof_A = kc * (1 + taur + taud);
|
||||
cof_B = kc * (1 + 2 * taud);
|
||||
cof_C = kc * taud;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute() **********************************************************************
|
||||
* This, as they say, is where the magic happens. this function should be called
|
||||
* every time "void loop()" executes. the function will decide for itself whether a new
|
||||
* pid Output needs to be computed
|
||||
*
|
||||
* Some notes for people familiar with the nuts and bolts of PID control:
|
||||
* - I used the Ideal form of the PID equation. mainly because I like IMC
|
||||
* tunings. lock in the I and D, and then just vary P to get more
|
||||
* aggressive or conservative
|
||||
*
|
||||
* - While this controller presented to the outside world as being a Reset Time
|
||||
* controller, when the user enters their tunings the I term is converted to
|
||||
* Reset Rate. I did this merely to avoid the div0 error when the user wants
|
||||
* to turn Integral action off.
|
||||
*
|
||||
* - Derivative on Measurement is being used instead of Derivative on Error. The
|
||||
* performance is identical, with one notable exception. DonE causes a kick in
|
||||
* the controller output whenever there's a setpoint change. DonM does not.
|
||||
*
|
||||
* If none of the above made sense to you, and you would like it to, go to:
|
||||
* http://www.controlguru.com . Dr. Cooper was my controls professor, and is
|
||||
* gifted at concisely and clearly explaining PID control
|
||||
*********************************************************************************/
|
||||
void PID::Compute()
|
||||
{
|
||||
justCalced=false;
|
||||
if (!inAuto) return; //if we're in manual just leave;
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
//millis() wraps around to 0 at some point. depending on the version of the
|
||||
//Arduino Program you are using, it could be in 9 hours or 50 days.
|
||||
//this is not currently addressed by this algorithm.
|
||||
|
||||
|
||||
//...Perform PID Computations if it's time...
|
||||
if (now>=nextCompTime)
|
||||
{
|
||||
|
||||
Err = *mySetpoint - *myInput;
|
||||
//if we're using an external bias (i.e. the user used the
|
||||
//overloaded constructor,) then pull that in now
|
||||
if(UsingFeedForward)
|
||||
{
|
||||
bias = *myBias - outMin;
|
||||
}
|
||||
|
||||
|
||||
// perform the PID calculation.
|
||||
//float output = bias + kc * ((Err - lastErr)+ (taur * Err) + (taud * (Err - 2*lastErr + prevErr)));
|
||||
noInterrupts();
|
||||
int output = bias + (cof_A * Err - cof_B * lastErr + cof_C * prevErr);
|
||||
interrupts();
|
||||
|
||||
//make sure the computed output is within output constraints
|
||||
if (output < -outSpan) output = -outSpan;
|
||||
else if (output > outSpan) output = outSpan;
|
||||
|
||||
|
||||
prevErr = lastErr;
|
||||
lastErr = Err;
|
||||
|
||||
|
||||
//scale the output from percent span back out to a real world number
|
||||
*myOutput = output;
|
||||
|
||||
nextCompTime += tSample; // determine the next time the computation
|
||||
if(nextCompTime < now) nextCompTime = now + tSample; // should be performed
|
||||
|
||||
justCalced=true; //set the flag that will tell the outside world that the output was just computed
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* STATUS SECTION
|
||||
* These functions allow the outside world to query the status of the PID
|
||||
*****************************************************************************/
|
||||
|
||||
bool PID::JustCalculated()
|
||||
{
|
||||
return justCalced;
|
||||
}
|
||||
int PID::GetMode()
|
||||
{
|
||||
if(inAuto)return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int PID::GetINMin()
|
||||
{
|
||||
return inMin;
|
||||
}
|
||||
int PID::GetINMax()
|
||||
{
|
||||
return inMin + inSpan;
|
||||
}
|
||||
int PID::GetOUTMin()
|
||||
{
|
||||
return outMin;
|
||||
}
|
||||
int PID::GetOUTMax()
|
||||
{
|
||||
return outMin+outSpan;
|
||||
}
|
||||
int PID::GetSampleTime()
|
||||
{
|
||||
return tSample;
|
||||
}
|
||||
float PID::GetP_Param()
|
||||
{
|
||||
return P_Param;
|
||||
}
|
||||
float PID::GetI_Param()
|
||||
{
|
||||
return I_Param;
|
||||
}
|
||||
|
||||
float PID::GetD_Param()
|
||||
{
|
||||
return D_Param;
|
||||
}
|
||||
|
113
src/dcaitirobot/embedded/lib/PID_Beta6/PID_Beta6.h
Normal file
113
src/dcaitirobot/embedded/lib/PID_Beta6/PID_Beta6.h
Normal file
@ -0,0 +1,113 @@
|
||||
#ifndef PID_Beta6_h
|
||||
#define PID_Beta6_h
|
||||
|
||||
class PID
|
||||
{
|
||||
|
||||
|
||||
public:
|
||||
|
||||
#define AUTO 1
|
||||
#define MANUAL 0
|
||||
#define LIBRARY_VERSION 0.6
|
||||
|
||||
//commonly used functions **************************************************************************
|
||||
PID(int*, int*, int*, // * constructor. links the PID to the Input, Output, and
|
||||
float, float, float); // Setpoint. Initial tuning parameters are also set here
|
||||
|
||||
PID(int*, int*, int*, // * Overloaded Constructor. if the user wants to implement
|
||||
int*, float, float, float); // feed-forward
|
||||
|
||||
void SetMode(int Mode); // * sets PID to either Manual (0) or Auto (non-0)
|
||||
|
||||
void Compute(); // * performs the PID calculation. it should be
|
||||
// called every time loop() cycles. ON/OFF and
|
||||
// calculation frequency can be set using SetMode
|
||||
// SetSampleTime respectively
|
||||
|
||||
void SetInputLimits(int, int); //Tells the PID what 0-100% are for the Input
|
||||
|
||||
void SetOutputLimits(int, int); //Tells the PID what 0-100% are for the Output
|
||||
|
||||
|
||||
//available but not commonly used functions ********************************************************
|
||||
void SetTunings(float, float, // * While most users will set the tunings once in the
|
||||
float); // constructor, this function gives the user the option
|
||||
// of changing tunings during runtime for Adaptive control
|
||||
|
||||
void SetSampleTime(int); // * sets the frequency, in Milliseconds, with which
|
||||
// the PID calculation is performed. default is 1000
|
||||
|
||||
void Reset(); // * reinitializes controller internals. automatically
|
||||
// called on a manual to auto transition
|
||||
|
||||
bool JustCalculated(); // * in certain situations, it helps to know when the PID has
|
||||
// computed this bit will be true for one cycle after the
|
||||
// pid calculation has occurred
|
||||
|
||||
|
||||
//Status functions allow you to query current PID constants ***************************************
|
||||
int GetMode();
|
||||
int GetINMin();
|
||||
int GetINMax();
|
||||
int GetOUTMin();
|
||||
int GetOUTMax();
|
||||
int GetSampleTime();
|
||||
float GetP_Param();
|
||||
float GetI_Param();
|
||||
float GetD_Param();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void ConstructorCommon(int*, int*, int*, // * code that is shared by the constructors
|
||||
float, float, float);
|
||||
|
||||
//scaled, tweaked parameters we'll actually be using
|
||||
float kc; // * (P)roportional Tuning Parameter
|
||||
float taur; // * (I)ntegral Tuning Parameter
|
||||
float taud; // * (D)erivative Tuning Parameter
|
||||
|
||||
float cof_A;
|
||||
float cof_B;
|
||||
float cof_C;
|
||||
|
||||
//nice, pretty parameters we'll give back to the user if they ask what the tunings are
|
||||
float P_Param;
|
||||
float I_Param;
|
||||
float D_Param;
|
||||
|
||||
|
||||
int *myInput; // * Pointers to the Input, Output, and Setpoint variables
|
||||
int *myOutput; // This creates a hard link between the variables and the
|
||||
int *mySetpoint; // PID, freeing the user from having to constantly tell us
|
||||
// what these values are. with pointers we'll just know.
|
||||
|
||||
int *myBias; // * Pointer to the External FeedForward bias, only used
|
||||
// if the advanced constructor is used
|
||||
bool UsingFeedForward; // * internal flag that tells us if we're using FeedForward or not
|
||||
|
||||
unsigned long nextCompTime; // * Helps us figure out when the PID Calculation needs to
|
||||
// be performed next
|
||||
// to determine when to compute next
|
||||
unsigned long tSample; // * the frequency, in milliseconds, with which we want the
|
||||
// the PID calculation to occur.
|
||||
bool inAuto; // * Flag letting us know if we are in Automatic or not
|
||||
|
||||
// the derivative required for the D term
|
||||
//float accError; // * the (I)ntegral term is based on the sum of error over
|
||||
// time. this variable keeps track of that
|
||||
float bias; // * the base output from which the PID operates
|
||||
|
||||
int Err;
|
||||
int lastErr;
|
||||
int prevErr;
|
||||
|
||||
float inMin, inSpan; // * input and output limits, and spans. used convert
|
||||
float outMin, outSpan; // real world numbers into percent span, with which
|
||||
// the PID algorithm is more comfortable.
|
||||
|
||||
bool justCalced; // * flag gets set for one cycle after the pid calculates
|
||||
};
|
||||
#endif
|
||||
|
BIN
src/dcaitirobot/embedded/lib/PID_Beta6/PID_Beta6.o
Normal file
BIN
src/dcaitirobot/embedded/lib/PID_Beta6/PID_Beta6.o
Normal file
Binary file not shown.
66
src/dcaitirobot/embedded/lib/PID_Beta6/fuzzy_table.h
Normal file
66
src/dcaitirobot/embedded/lib/PID_Beta6/fuzzy_table.h
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
#if 0
|
||||
#ifndef _PID_TABLE_H_
|
||||
#define _PID_TABLE_H_
|
||||
|
||||
#define PB 6
|
||||
#define PM 5
|
||||
#define PS 4
|
||||
#define ZO 3
|
||||
#define NS 2
|
||||
#define NM 1
|
||||
#define NB 0
|
||||
|
||||
#define DELTA_KP_IDX 0
|
||||
#define DELATA_KI_IDX 1
|
||||
#define ERR 7
|
||||
#define DELTA_ERR 7
|
||||
|
||||
|
||||
const char pid_tbl[2][7][7]={
|
||||
{
|
||||
{PB,PB,PM,PM,PS,ZO,ZO},
|
||||
{PB,PB,PM,PS,PS,ZO,ZO},
|
||||
{PM,PM,PM,PS,ZO,NS,NS},
|
||||
{PM,PM,PS,ZO,NS,NM,NM},
|
||||
{PS,PS,ZO,NS,NS,NM,NM},
|
||||
{PS,ZO,NS,NM,NM,NM,NB},
|
||||
{ZO,ZO,NM,NM,NM,NB,NB},
|
||||
},
|
||||
|
||||
{
|
||||
{NB,NB,NM,NM,NS,ZO,ZO},
|
||||
{NB,NB,NM,NS,NS,ZO,ZO},
|
||||
{NB,NM,NS,NS,ZO,PS,PS},
|
||||
{NM,NM,NS,ZO,PS,PM,PM},
|
||||
{NM,NS,ZO,PS,PS,PM,PB},
|
||||
{ZO,ZO,PS,PS,PM,PB,PB},
|
||||
{ZO,ZO,PS,PM,PM,PB,PB},
|
||||
},
|
||||
};
|
||||
|
||||
const float kp_tbl[ERR] = {
|
||||
0.07987,
|
||||
0.08974,
|
||||
0.09948,
|
||||
0.12922,
|
||||
0.54896,
|
||||
0.70000,
|
||||
0.76000,
|
||||
};
|
||||
|
||||
|
||||
const float ki_tbl[DELTA_ERR] = {
|
||||
0.02451,
|
||||
0.07353,
|
||||
0.14706,
|
||||
0.19608,
|
||||
0.24510,
|
||||
0.30000,
|
||||
0.00000,
|
||||
};
|
||||
|
||||
#endif _PID_TABLE_H_
|
||||
|
||||
#endif
|
||||
|
38
src/dcaitirobot/embedded/lib/PID_Beta6/keywords.txt
Normal file
38
src/dcaitirobot/embedded/lib/PID_Beta6/keywords.txt
Normal file
@ -0,0 +1,38 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For LiquidCrystal
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
PID KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
SetMode KEYWORD2
|
||||
Compute KEYWORD2
|
||||
SetInputLimits KEYWORD2
|
||||
SetOutputLimits KEYWORD2
|
||||
SetTunings KEYWORD2
|
||||
SetSampleTime KEYWORD2
|
||||
JustCalculated KEYWORD2
|
||||
Reset KEYWORD2
|
||||
GetMode KEYWORD2
|
||||
GetINMin KEYWORD2
|
||||
GetINMax KEYWORD2
|
||||
GetOUTMin KEYWORD2
|
||||
GetOUTMax KEYWORD2
|
||||
GetSampleTime KEYWORD2
|
||||
GetP_Param KEYWORD2
|
||||
GetI_Param KEYWORD2
|
||||
GetD_Param KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
AUTO LITERAL1
|
||||
MANUAL LITERAL1
|
191
src/dcaitirobot/embedded/lib/PinChangeInt/PinChangeInt.cpp
Normal file
191
src/dcaitirobot/embedded/lib/PinChangeInt/PinChangeInt.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
PinChangeInt.cpp
|
||||
*/
|
||||
|
||||
#include <PinChangeInt.h>
|
||||
|
||||
|
||||
PCintPort::PCintPin PCintPort::PCintPin::pinDataAlloc[MAX_PIN_CHANGE_PINS];
|
||||
|
||||
|
||||
PCintPort PCintPort::pcIntPorts[] = {
|
||||
PCintPort(0,PCMSK0),
|
||||
PCintPort(1,PCMSK1),
|
||||
PCintPort(2,PCMSK2)
|
||||
};
|
||||
|
||||
void PCintPort::addPin(uint8_t mode,uint8_t mask,PCIntvoidFuncPtr userFunc)
|
||||
{
|
||||
//int i = 0;
|
||||
PCintPin* p = PCintPin::pinDataAlloc;
|
||||
do {
|
||||
if (!p->PCintMode) { // found an available pin data structure
|
||||
// now find the next slot to put it in
|
||||
PCintPin** t = &pcIntPins[0];
|
||||
do {
|
||||
if (!*t) { // we have a slot
|
||||
// fill in the data
|
||||
p->PCintMode = mode;
|
||||
*t = p;
|
||||
p->PCintFunc = userFunc;
|
||||
// set the mask
|
||||
pcmask |= p->PCIntMask = mask;
|
||||
// enable the interrupt
|
||||
PCICR |= PCICRbit;
|
||||
|
||||
#ifdef DEBUG
|
||||
Serial.print("BitMask = ");
|
||||
Serial.print(mask, BIN);
|
||||
Serial.print("; ");
|
||||
Serial.print("slot = ");
|
||||
Serial.println((int)(t - &pcIntPins[0]), DEC);
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
} while (int(++t) < int(&pcIntPins[8]));
|
||||
}
|
||||
} while (int(++p) < int(&PCintPin::pinDataAlloc[MAX_PIN_CHANGE_PINS]));
|
||||
}
|
||||
|
||||
void PCintPort::delPin(uint8_t mask)
|
||||
{
|
||||
PCintPin** t = pcIntPins;
|
||||
while (*t) {
|
||||
PCintPin& p = **t;
|
||||
if (p.PCIntMask == mask) { // found the target
|
||||
uint8_t oldSREG = SREG;
|
||||
cli();
|
||||
// disable the mask.
|
||||
pcmask &= ~mask;
|
||||
// if that's the last one, disable the interrupt.
|
||||
if (pcmask == 0) {
|
||||
PCICR &= ~(PCICRbit);
|
||||
}
|
||||
p.PCIntMask = 0;
|
||||
p.PCintMode = 0;
|
||||
p.PCintFunc = NULL;
|
||||
do { // shuffle down as we pass through the list, filling the hole
|
||||
*t = t[1];
|
||||
} while (*t);
|
||||
SREG = oldSREG;
|
||||
return;
|
||||
}
|
||||
t++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* attach an interrupt to a specific pin using pin change interrupts.
|
||||
*/
|
||||
void PCintPort::attachInterrupt(uint8_t pin, PCIntvoidFuncPtr userFunc, int mode)
|
||||
{
|
||||
uint8_t portNum = digitalPinToPort(pin);
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
if (((portNum != 2) && (portNum != 11))
|
||||
|| (userFunc == NULL)
|
||||
) {
|
||||
|
||||
#ifdef DEBUG
|
||||
Serial.println("NOT_A_PORT det");
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
// map pin to PCIR register
|
||||
uint8_t portIndex = (portNum == 2)?(0):(2);
|
||||
#else
|
||||
if ((portNum == NOT_A_PORT) || (userFunc == NULL)) {
|
||||
return;
|
||||
}
|
||||
// map pin to PCIR register
|
||||
uint8_t portIndex = portNum - 2;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
Serial.print("portNum = ");
|
||||
Serial.print(portNum, DEC);
|
||||
Serial.print("; ");
|
||||
#endif
|
||||
|
||||
PCintPort &port = PCintPort::pcIntPorts[portIndex];
|
||||
port.addPin(mode,digitalPinToBitMask(pin),userFunc);
|
||||
}
|
||||
|
||||
void PCintPort::detachInterrupt(uint8_t pin)
|
||||
{
|
||||
uint8_t portNum = digitalPinToPort(pin);
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
if ((portNum != 2) && (portNum != 11)) {
|
||||
//Serial.println("NOT_A_PORT det");
|
||||
return;
|
||||
}
|
||||
uint8_t portIndex = (portNum == 2)?(0):(2);
|
||||
PCintPort& port = PCintPort::pcIntPorts[portIndex];
|
||||
|
||||
#else
|
||||
if (portNum == NOT_A_PORT) {
|
||||
//Serial.println("NOT_A_PORT det");
|
||||
return;
|
||||
}
|
||||
PCintPort& port = PCintPort::pcIntPorts[portNum - 2];
|
||||
#endif
|
||||
|
||||
port.delPin(digitalPinToBitMask(pin));
|
||||
}
|
||||
|
||||
// common code for isr handler. "port" is the PCINT number.
|
||||
// there isn't really a good way to back-map ports and masks to pins.
|
||||
void PCintPort::PCint() {
|
||||
#ifndef DISABLE_PCINT_MULTI_SERVICE
|
||||
uint8_t pcifr;
|
||||
do {
|
||||
#endif
|
||||
uint8_t curr = portInputReg;
|
||||
uint8_t mask = curr ^ PCintLast;
|
||||
// get the pin states for the indicated port.
|
||||
// mask is pins that have changed. screen out non pcint pins.
|
||||
if ((mask &= pcmask) == 0) {
|
||||
return;
|
||||
}
|
||||
PCintLast = curr;
|
||||
PCintPin** t = pcIntPins;
|
||||
while (*t) {
|
||||
PCintPin& p = **t;
|
||||
if (p.PCIntMask & mask) { // a changed bit
|
||||
// Trigger interrupt if mode is CHANGE, or if mode is RISING and
|
||||
// the bit is currently high, or if mode is FALLING and bit is low.
|
||||
if (p.PCintMode == CHANGE
|
||||
|| ((p.PCintMode == FALLING) && !(curr & p.PCIntMask))
|
||||
|| ((p.PCintMode == RISING) && (curr & p.PCIntMask))
|
||||
) {
|
||||
p.PCintFunc();
|
||||
}
|
||||
}
|
||||
t++;
|
||||
}
|
||||
#ifndef DISABLE_PCINT_MULTI_SERVICE
|
||||
pcifr = PCIFR & PCICRbit;
|
||||
PCIFR = pcifr; // clear the interrupt if we will process is (no effect if bit is zero)
|
||||
} while(pcifr);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef NO_PORTB_PINCHANGES
|
||||
ISR(PCINT0_vect) {
|
||||
PCintPort::pcIntPorts[0].PCint();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_PORTC_PINCHANGES
|
||||
ISR(PCINT1_vect) {
|
||||
PCintPort::pcIntPorts[1].PCint();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_PORTD_PINCHANGES
|
||||
ISR(PCINT2_vect) {
|
||||
PCintPort::pcIntPorts[2].PCint();
|
||||
}
|
||||
#endif
|
||||
|
136
src/dcaitirobot/embedded/lib/PinChangeInt/PinChangeInt.h
Normal file
136
src/dcaitirobot/embedded/lib/PinChangeInt/PinChangeInt.h
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
PinChangeInt.h
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PinChangeInt_h
|
||||
#define PinChangeInt_h
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include "PinChangeIntConfig.h"
|
||||
#ifndef Pins_Arduino_h
|
||||
#include "pins_arduino.h"
|
||||
#endif
|
||||
// This library was inspired by and derived from "johnboiles" (it seems) Main.PCInt Arduino Playground example
|
||||
// see: http://www.arduino.cc/playground/Main/PcInt
|
||||
|
||||
|
||||
// Interrupt management for PCI
|
||||
/*
|
||||
* an extension to the interrupt support for arduino.
|
||||
* add pin change interrupts to the external interrupts, giving a way
|
||||
* for users to have interrupts drive off of any pin.
|
||||
* Refer to avr-gcc header files, arduino source and atmega datasheet.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Theory: all IO pins on AtmegaX(168/328/1280/2560) are covered by Pin Change Interrupts.
|
||||
* The PCINT corresponding to the pin must be enabled and masked, and
|
||||
* an ISR routine provided. Since PCINTs are per port, not per pin, the ISR
|
||||
* must use some logic to actually implement a per-pin interrupt service.
|
||||
*/
|
||||
|
||||
/* Pin to interrupt map ON ATmega168/328:
|
||||
* D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
|
||||
* D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
|
||||
* A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
|
||||
*/
|
||||
|
||||
|
||||
/* Pin to interrupt map ON ATmega1280/2560:
|
||||
* D50-D53 = PCINT 3-0 = PCIR0 = PB = PCIE0 = pcmsk0
|
||||
* D10-D13 = PCINT 4-7 = PCIR0 = PB = PCIE0 = pcmsk0
|
||||
* A8-A15 (D62-D69) = PCINT 16-23 = PCIR2 = Pk = PCIE2 = pcmsk2
|
||||
|
||||
* *******D0 = PCINT 8 = PCIR1 = PE = PCIE1 = pcmsk1**********
|
||||
* *******D14-D15 = PCINT 10-9 = PJ = PCIE1 = pcmsk1*********
|
||||
********NOTE:PCINT 8-15 does NOT available in this library******
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Please make any configuration changes in the accompanying PinChangeIntConfig.h file.
|
||||
This will help avoid having to reset your config in the event of changes to the
|
||||
library code (just don't replace that file when you update).
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MAX_PIN_CHANGE_PINS
|
||||
#define MAX_PIN_CHANGE_PINS 8
|
||||
#endif
|
||||
|
||||
// You can reduce the memory footprint of this handler by declaring that there will be no pin change interrupts
|
||||
// on any of the three ports.
|
||||
// define NO_PORTB_PINCHANGES to indicate that port b will not be used for pin change interrupts
|
||||
// define NO_PORTC_PINCHANGES to indicate that port c will not be used for pin change interrupts
|
||||
// define NO_PORTD_PINCHANGES to indicate that port d will not be used for pin change interrupts
|
||||
// If only a single port remains, the handler will be declared inline reducing the size and latency
|
||||
// of the handler.
|
||||
|
||||
// if their is only one PCInt vector in use the code can be inlined
|
||||
// reducing latecncy and code size
|
||||
#define INLINE_PCINT
|
||||
#if ((defined(NO_PORTB_PINCHANGES) && defined(NO_PORTC_PINCHANGES)) || \
|
||||
(defined(NO_PORTC_PINCHANGES) && defined(NO_PORTD_PINCHANGES)) || \
|
||||
(defined(NO_PORTD_PINCHANGES) && defined(NO_PORTB_PINCHANGES)))
|
||||
#undef INLINE_PCINT
|
||||
#define INLINE_PCINT inline
|
||||
#endif
|
||||
|
||||
// Provide drop in compatibility with johnboiles PCInt project at
|
||||
// http://www.arduino.cc/playground/Main/PcInt
|
||||
#define PCdetachInterrupt(pin) PCintPort::detachInterrupt(pin)
|
||||
#define PCattachInterrupt(pin,userFunc,mode) PCintPort::attachInterrupt(pin, userFunc,mode)
|
||||
|
||||
|
||||
|
||||
typedef void (*PCIntvoidFuncPtr)(void);
|
||||
|
||||
class PCintPort {
|
||||
PCintPort(int index,volatile uint8_t& maskReg) :
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
portInputReg(*portInputRegister((index == 0)?(2):(index + 9))),
|
||||
|
||||
#else
|
||||
portInputReg(*portInputRegister(index + 2)),
|
||||
#endif
|
||||
|
||||
pcmask(maskReg),
|
||||
PCICRbit(1 << index),
|
||||
PCintLast(0) {
|
||||
for (int i = 0; i < 9; i++) {
|
||||
pcIntPins[i] = NULL;
|
||||
}
|
||||
}
|
||||
public:
|
||||
static void attachInterrupt(uint8_t pin, PCIntvoidFuncPtr userFunc, int mode);
|
||||
static void detachInterrupt(uint8_t pin);
|
||||
INLINE_PCINT void PCint();
|
||||
static PCintPort pcIntPorts[];
|
||||
|
||||
protected:
|
||||
class PCintPin {
|
||||
public:
|
||||
PCintPin() :
|
||||
PCintFunc((PCIntvoidFuncPtr)NULL),
|
||||
PCintMode(0) {}
|
||||
PCIntvoidFuncPtr PCintFunc;
|
||||
uint8_t PCintMode;
|
||||
uint8_t PCIntMask;
|
||||
static PCintPin pinDataAlloc[MAX_PIN_CHANGE_PINS];
|
||||
};
|
||||
void addPin(uint8_t mode,uint8_t mask,PCIntvoidFuncPtr userFunc);
|
||||
void delPin(uint8_t mask);
|
||||
volatile uint8_t& portInputReg;
|
||||
volatile uint8_t& pcmask;
|
||||
const uint8_t PCICRbit;
|
||||
uint8_t PCintLast;
|
||||
PCintPin* pcIntPins[9]; // extra entry is a barrier
|
||||
};
|
||||
#endif
|
@ -0,0 +1,20 @@
|
||||
|
||||
// This file is used to separate the changes you make to personalize the
|
||||
// Pin Change Interrupt library from any future changes to the library itself.
|
||||
// Ideally it would reside in the folder of the current sketch, but I have not
|
||||
// figured out how such a file can be included from a library.
|
||||
// Nothing is required to be defined within this file since default values are
|
||||
// defined in the primary PinChangeInt.h file.
|
||||
|
||||
// Uncomment the line below to limit the handler to servicing a single interrupt per invocation.
|
||||
//#define DISABLE_PCINT_MULTI_SERVICE
|
||||
|
||||
// Define the value MAX_PIN_CHANGE_PINS to limit the number of pins that may be
|
||||
// used for pin change interrupts. This value determines the number of pin change
|
||||
// interrupts supported for all ports.
|
||||
//#define MAX_PIN_CHANGE_PINS 2
|
||||
|
||||
// declare ports without pin change interrupts used
|
||||
//#define NO_PORTB_PINCHANGES
|
||||
//#define NO_PORTC_PINCHANGES
|
||||
//#define NO_PORTD_PINCHANGES
|
46
src/dcaitirobot/embedded/lib/README
Normal file
46
src/dcaitirobot/embedded/lib/README
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
@ -13,5 +13,3 @@ platform = atmelavr
|
||||
board = diecimilaatmega328
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
arduino-libraries
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "communication/UValue.hpp"
|
||||
#include "misc/Utils.hpp"
|
||||
#include "constants.h"
|
||||
#include "MotorWheel.h"
|
||||
|
||||
uint8_t UARTCom::data[MAX_PACKET_LENGTH];
|
||||
uint8_t currentParseIndex;
|
||||
@ -170,7 +171,7 @@ void handleRequest (uint8_t* d)
|
||||
break;
|
||||
}
|
||||
case DRIVE_ENCODER: {
|
||||
UValue::sendBothEncoderValues(-wheelLeft.getGearedSpeedRPM(), wheelRight.getGearedSpeedRPM());
|
||||
UValue::sendBothEncoderValues(-wheelLeft.getGearedSpeedRPM(), wheelRight.getGearedSpeedRPM(), wheelLeft.getPosition(), wheelRight.getPosition());
|
||||
break;
|
||||
}
|
||||
case PID_PARAMETER: {
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "communication/UCommands.hpp"
|
||||
#include "misc/Utils.hpp"
|
||||
#include "MotorWheel.h"
|
||||
|
||||
extern MotorWheel wheelLeft;
|
||||
extern MotorWheel wheelRight;
|
||||
|
||||
|
@ -19,20 +19,24 @@ void UValue::sendEncoderValue (U_Component cp, float value)
|
||||
UARTCom::send(VALUE, 4, cp, switched_b);
|
||||
}
|
||||
|
||||
void UValue::sendBothEncoderValues (float value_right, float value_left)
|
||||
void UValue::sendBothEncoderValues (float speed_right, float speed_left, float pos_right, float pos_left)
|
||||
{
|
||||
union {
|
||||
float f[2];
|
||||
uint8_t b[8];
|
||||
float f[4];
|
||||
uint8_t b[16];
|
||||
} u;
|
||||
u.f[0] = value_right;
|
||||
u.f[1] = value_left;
|
||||
u.f[0] = speed_right;
|
||||
u.f[1] = speed_left;
|
||||
u.f[2] = pos_right;
|
||||
u.f[3] = pos_left;
|
||||
|
||||
uint8_t switched_byte_array[8];
|
||||
uint8_t switched_byte_array[16];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
switched_byte_array[i] = u.b[3 - i];
|
||||
switched_byte_array[i+4] = u.b[7 - i];
|
||||
switched_byte_array[i+8] = u.b[11 - i];
|
||||
switched_byte_array[i+12] = u.b[15 - i];
|
||||
}
|
||||
|
||||
UARTCom::send(VALUE, 8, ALL_MOTORS, switched_byte_array);
|
||||
UARTCom::send(VALUE, 16, ALL_MOTORS, switched_byte_array);
|
||||
}
|
14
src/dcaitirobot/launch/frames.gv
Normal file
14
src/dcaitirobot/launch/frames.gv
Normal file
@ -0,0 +1,14 @@
|
||||
digraph G {
|
||||
"base_link" -> "velodyne"[label=" Broadcaster: default_authority\nAverage rate: 10000.0\nBuffer length: 0.0\nMost recent transform: 0.0\nOldest transform: 0.0\n"];
|
||||
"base_link" -> "base_footprint"[label=" Broadcaster: default_authority\nAverage rate: 10000.0\nBuffer length: 0.0\nMost recent transform: 0.0\nOldest transform: 0.0\n"];
|
||||
"chassis" -> "camera_link"[label=" Broadcaster: default_authority\nAverage rate: 10000.0\nBuffer length: 0.0\nMost recent transform: 0.0\nOldest transform: 0.0\n"];
|
||||
"base_link" -> "chassis"[label=" Broadcaster: default_authority\nAverage rate: 10000.0\nBuffer length: 0.0\nMost recent transform: 0.0\nOldest transform: 0.0\n"];
|
||||
"camera_link" -> "camera_link_optical"[label=" Broadcaster: default_authority\nAverage rate: 10000.0\nBuffer length: 0.0\nMost recent transform: 0.0\nOldest transform: 0.0\n"];
|
||||
"chassis" -> "caster_wheel_back"[label=" Broadcaster: default_authority\nAverage rate: 10000.0\nBuffer length: 0.0\nMost recent transform: 0.0\nOldest transform: 0.0\n"];
|
||||
"chassis" -> "caster_wheel_front"[label=" Broadcaster: default_authority\nAverage rate: 10000.0\nBuffer length: 0.0\nMost recent transform: 0.0\nOldest transform: 0.0\n"];
|
||||
"chassis" -> "laser_frame"[label=" Broadcaster: default_authority\nAverage rate: 10000.0\nBuffer length: 0.0\nMost recent transform: 0.0\nOldest transform: 0.0\n"];
|
||||
edge [style=invis];
|
||||
subgraph cluster_legend { style=bold; color=black; label ="view_frames Result";
|
||||
"Recorded at time: 1688907915.1191485"[ shape=plaintext ] ;
|
||||
}->"base_link";
|
||||
}
|
BIN
src/dcaitirobot/launch/frames.pdf
Normal file
BIN
src/dcaitirobot/launch/frames.pdf
Normal file
Binary file not shown.
50
src/dcaitirobot/launch/launch_slam.py
Normal file
50
src/dcaitirobot/launch/launch_slam.py
Normal file
@ -0,0 +1,50 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import IncludeLaunchDescription
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch_ros.actions import Node
|
||||
from ament_index_python import get_package_share_directory
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
slam_params = os.path.join(
|
||||
str(Path(__file__).absolute().parent.parent), # <-- Replace with your package name
|
||||
'config',
|
||||
'slam.yml'
|
||||
)
|
||||
|
||||
slam_toolbox = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource([os.path.join(
|
||||
get_package_share_directory('slam_toolbox'),'launch','online_async_launch.py'
|
||||
)]),
|
||||
launch_arguments={
|
||||
'use_sim_time': 'true',
|
||||
'slam_params_file': slam_params}.items()
|
||||
)
|
||||
|
||||
node_fix_lidar = Node(
|
||||
package="dcaitirobot",
|
||||
executable="fixlidar",
|
||||
name="fixlidar",
|
||||
)
|
||||
|
||||
node_scale_odom = Node(
|
||||
package="dcaitirobot",
|
||||
executable="scaleodom",
|
||||
name="scaleodom",
|
||||
)
|
||||
|
||||
tfs1 = Node(package = "tf2_ros",
|
||||
executable = "static_transform_publisher",
|
||||
arguments = ["0", "0", "0" ,"0", "0", "0", "base_link", "velodyne"])
|
||||
|
||||
tfs2 = Node(package = "tf2_ros",
|
||||
executable = "static_transform_publisher",
|
||||
arguments = ["0", "0", "0" ,"0", "0", "0", "base_link", "base_footprint"])
|
||||
|
||||
|
||||
return LaunchDescription([
|
||||
slam_toolbox, tfs1, tfs2, node_fix_lidar, node_scale_odom
|
||||
])
|
@ -28,17 +28,17 @@ def generate_launch_description():
|
||||
)
|
||||
)
|
||||
),
|
||||
IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
str(
|
||||
(
|
||||
Path(get_package_share_directory("slam_toolbox")).absolute()
|
||||
/ "launch"
|
||||
/ "online_async_launch.py"
|
||||
)
|
||||
)
|
||||
),
|
||||
launch_arguments=[("use_sim_time", "True")],
|
||||
),
|
||||
# IncludeLaunchDescription(
|
||||
# PythonLaunchDescriptionSource(
|
||||
# str(
|
||||
# (
|
||||
# Path(get_package_share_directory("slam_toolbox")).absolute()
|
||||
# / "launch"
|
||||
# / "online_async_launch.py"
|
||||
# )
|
||||
# )
|
||||
# ),
|
||||
# launch_arguments=[("use_sim_time", "True")],
|
||||
# ),
|
||||
]
|
||||
)
|
||||
|
@ -12,7 +12,8 @@ setup(
|
||||
('share/ament_index/resource_index/packages',
|
||||
['resource/' + package_name]),
|
||||
('share/' + package_name, ['package.xml']),
|
||||
(os.path.join('share', package_name), glob('launch/*.launch.py'))
|
||||
(os.path.join('share', package_name), glob('launch/*.py')),
|
||||
(os.path.join('share', 'config'), glob('config/*.yml')),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
@ -27,6 +28,8 @@ setup(
|
||||
'twistcalc = dcaitirobot.twist:main',
|
||||
'serial_comms = dcaitirobot.serial_comms:main',
|
||||
'joy = dcaitirobot.joy:main',
|
||||
"fixlidar = dcaitirobot.handle_nan_scan:main",
|
||||
"scaleodom = dcaitirobot.scale_odom:main"
|
||||
],
|
||||
},
|
||||
)
|
||||
|
@ -34,11 +34,11 @@ public:
|
||||
|
||||
void setup(const std::string &serial_device, int32_t baud_rate, int32_t timeout_ms);
|
||||
void sendEmptyMsg();
|
||||
void readEncoderValues(double &val_1, double &val_2);
|
||||
void readEncoderValues(double &left_speed, double &right_speed, double &left_pos, double &right_pos);
|
||||
void setMotorValues(float val_1, float val_2);
|
||||
void setPidValues(float k_p, float k_d, float k_i, float k_o);
|
||||
const std::vector<uint8_t> buildMsg(uint8_t frame_type, uint8_t frame_component, long time, std::vector<uint8_t> payload);
|
||||
void readSpeed(double &left_speed, double &right_speed);
|
||||
void readSpeedandPosition(double &left_speed, double &right_speed, double &left_pos, double &right_pos);
|
||||
|
||||
bool connected() const { return serial_conn_.isOpen(); }
|
||||
|
||||
|
@ -20,12 +20,12 @@ void ArduinoComms::sendEmptyMsg()
|
||||
{
|
||||
}
|
||||
|
||||
void ArduinoComms::readEncoderValues(double &left_speed, double &right_speed)
|
||||
void ArduinoComms::readEncoderValues(double &left_speed, double &right_speed, double &left_pos, double &right_pos)
|
||||
{
|
||||
std::vector<uint8_t> payload = {DRIVE_ENCODER};
|
||||
std::vector<uint8_t> msg = buildMsg(REQUEST, BOTH_MOTORS, 0, payload);
|
||||
sendMsg(msg);
|
||||
readSpeed(left_speed, right_speed);
|
||||
readSpeedandPosition(left_speed, right_speed, left_pos, right_pos);
|
||||
}
|
||||
|
||||
void ArduinoComms::setMotorValues(float left_speed, float right_speed)
|
||||
@ -48,7 +48,7 @@ void ArduinoComms::setPidValues(float k_p, float k_d, float k_i, float k_o)
|
||||
ss << "u " << k_p << ":" << k_d << ":" << k_i << ":" << k_o << "\r";
|
||||
}
|
||||
|
||||
void ArduinoComms::readSpeed(double &left_speed, double &right_speed)
|
||||
void ArduinoComms::readSpeedandPosition(double &left_speed, double &right_speed, double &left_pos, double &right_pos)
|
||||
{
|
||||
std::vector<uint8_t> buffer;
|
||||
int read_bytes = 0;
|
||||
@ -90,13 +90,21 @@ void ArduinoComms::readSpeed(double &left_speed, double &right_speed)
|
||||
if (frame_type == VALUE && frame_component == BOTH_MOTORS)
|
||||
{
|
||||
std::vector<uint8_t> left_speed_bytes(payload.begin(), payload.begin() + 4);
|
||||
std::vector<uint8_t> right_speed_bytes(payload.begin() + 4, payload.end());
|
||||
std::vector<uint8_t> right_speed_bytes(payload.begin() + 4, payload.begin()+8);
|
||||
std::vector<uint8_t> left_pos_bytes(payload.begin() + 8, payload.begin()+12);
|
||||
std::vector<uint8_t> right_pos_bytes(payload.begin() + 12, payload.end());
|
||||
std::reverse(left_speed_bytes.begin(), left_speed_bytes.end());
|
||||
std::reverse(right_speed_bytes.begin(), right_speed_bytes.end());
|
||||
std::reverse(left_pos_bytes.begin(), left_pos_bytes.end());
|
||||
std::reverse(right_pos_bytes.begin(), right_pos_bytes.end());
|
||||
float left_speed_float = *reinterpret_cast<float*>(&left_speed_bytes[0]);
|
||||
float right_speed_float = *reinterpret_cast<float*>(&right_speed_bytes[0]);
|
||||
float left_pos_float = *reinterpret_cast<float*>(&left_pos_bytes[0]);
|
||||
float right_pos_float = *reinterpret_cast<float*>(&right_pos_bytes[0]);
|
||||
left_speed = left_speed_float;
|
||||
right_speed = right_speed_float;
|
||||
left_pos = left_pos_float;
|
||||
right_pos = right_pos_float;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,11 @@
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include <math.h>
|
||||
|
||||
#define RADSTORPM(a)(a*60.0/(2.0*M_PI))
|
||||
#define RPMSTORADS(a)(a*2.0*M_PI/60.0)
|
||||
#define REVSTORAD(a)(a*2.0*M_PI)
|
||||
#define RADSTOREV(a)(a/(2.0*M_PI))
|
||||
|
||||
namespace diffdrive_arduino
|
||||
{
|
||||
|
||||
@ -106,18 +111,15 @@ return_type DiffDriveArduino::read()
|
||||
return return_type::ERROR;
|
||||
}
|
||||
|
||||
arduino_.readEncoderValues(l_wheel_.vel, r_wheel_.vel);
|
||||
// write l_wheel_.cmd to string and print via RCLCPP_INFO
|
||||
std::string msg = "Left Wheel Read: " + std::to_string(l_wheel_.vel);
|
||||
RCLCPP_INFO(logger_, msg.c_str());
|
||||
arduino_.readEncoderValues(l_wheel_.vel, r_wheel_.vel, l_wheel_.pos, r_wheel_.pos);
|
||||
// convert rpm to rad/s
|
||||
l_wheel_.vel = (l_wheel_.vel / 60) * 2 * M_PI * 0.049 ;
|
||||
r_wheel_.vel = (r_wheel_.vel / 60) * 2 * M_PI * 0.049 ;
|
||||
l_wheel_.vel = RPMSTORADS(l_wheel_.vel);
|
||||
r_wheel_.vel = RPMSTORADS(r_wheel_.vel);
|
||||
|
||||
|
||||
// Force the wheel position
|
||||
l_wheel_.pos = l_wheel_.pos + l_wheel_.vel * deltaSeconds;
|
||||
r_wheel_.pos = r_wheel_.pos + r_wheel_.vel * deltaSeconds;
|
||||
// convert rev to rad
|
||||
l_wheel_.pos = REVSTORAD(l_wheel_.pos);
|
||||
r_wheel_.pos = REVSTORAD(r_wheel_.pos);
|
||||
return return_type::OK;
|
||||
}
|
||||
|
||||
@ -128,9 +130,7 @@ return_type DiffDriveArduino::write()
|
||||
return return_type::ERROR;
|
||||
}
|
||||
// convert rad/s to rpm and write to motors
|
||||
arduino_.setMotorValues(l_wheel_.cmd * 30 / M_PI , r_wheel_.cmd * 30 / M_PI );
|
||||
std::string msg = "Left Wheel Command: " + std::to_string(l_wheel_.cmd* 30 / M_PI);
|
||||
RCLCPP_INFO(logger_, msg.c_str());
|
||||
arduino_.setMotorValues(RADSTORPM(l_wheel_.cmd), RADSTORPM(r_wheel_.cmd));
|
||||
|
||||
return return_type::OK;
|
||||
}
|
||||
|
1
src/xacro
Submodule
1
src/xacro
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 344e25b49f3a01a2389f0f30304ed667d68a82bd
|
Loading…
x
Reference in New Issue
Block a user