Skip to content

Wheel and CAN Signals#

This page gives the wheel and vehicle signals that the simulator outputs and the J1939 CAN bus of the Maxxum. It gives each parameter group with its bytes, scale and offset, the command frames, the CAN transport and the recorded CAN file.

Scope and Assumptions#

The vehicle model computes the signals at each physics step of 1/120 s. The simulator outputs them on four paths.

Path Content Rate
Session log tractor.csv All signals of the vehicle, the wheels and the implement. 120 Hz
Sensor episode truth.jsonl and actions.jsonl with a subset of the signals, can.jsonl with two CAN frames. 120 Hz, 20 Hz
Vehicle bridge One JSON observation with the signals that a controller needs. 10 Hz
CAN bus The same observation as J1939 frames, and command frames from a controller. 10 Hz

The CAN bus uses these assumptions.

  • The bus is a simulator interface in the format of SAE J1939 and ISO 11783. It is not a complete network.
  • The bus has no arbitration, no address claim and no transport protocol. Each frame has 8 data bytes.
  • The signals have no noise. A frame contains the exact value of the model after scaling and rounding.
  • The frames are for the Maxxum. The Polaris uses the drive-by-wire reports of the Drive-by-Wire and ULC page.
  • The game accepts command frames from one source address only: the controller at 0x2A.

The Vehicle Bridge page gives the JSON protocol. The Session Log page gives the columns of tractor.csv.

Simulated J1939 bus of the Maxxum: the game encodes the vehicle signals into frames, the CAN transport carries them to the ROS 2 bridge or to a controller, and the command frames come back. GAME, MAXXUM Vehicle signals engine, wheels, steering, pose, slip, soil, implement each 0.1 s PGN encoder SendCan() 13 frames, 0xFF1F last To the controller: EEC1, WBSD, Guidance Machine Status, Proprietary B 0xFF10 to 0xFF18, 0xFF1F. To the tractor: Guidance System Command, TSC1, XBR, Proprietary A. Vehicle controls steering, throttle, brake, differential lock, reset applied by arrival time PGN decoder HandleCanFrame() source address 0x2A only Sensor recorder 2 frames at can.hz, 20 Hz can.jsonl EEC1 and PGN 65280 CAN transport -RlCanUdp=<port> UDP on 127.0.0.1, 16-byte can_frame -RlCan=<interface> SocketCAN can/rx ROS 2 bridge sim_bridge can_msgs/Frame can/tx Controller ROS 2 node or ECU, address 0x2A In lockstep, the frame 0xFF1E ends the frames of each step request. The ROS 2 bridge counts this frame and does not publish it. A controller without ROS 2 connects directly to the UDP port or to the SocketCAN interface.
The simulated J1939 bus of the Maxxum. The game encodes the vehicle signals, and a controller sends the command frames. Open the diagram

Symbols#

Symbol Quantity Unit
\(r\) Raw value of a field: an unsigned or signed integer in the frame -
\(s\), \(o\) Scale and offset of a field unit per bit, unit
\(x\) Physical value of a field unit of the field
\(\kappa\) Path curvature, positive to the left 1/km on the bus
\(\delta\) Steering angle of the bicycle model, positive to the left rad
\(\delta_{max}\) Steering limit of the vehicle, 0.6632 rad for the Maxxum rad
\(L\) Wheelbase, 2.6416 m for the Maxxum m
\(v\) Forward speed of the chassis m/s
\(n_e\) Engine speed rev/min
\(u_t\), \(u_b\) Throttle command and brake command 0 to 1
\(a_x\) Acceleration demand of the brake frame m/s²

Vehicle Signals#

The table gives each signal with its source field and its name on each path. A dash shows that the path does not carry the signal. The source fields are in FAcresVehicleTelemetry: the wheel fields in Wheels[i], the control fields in Input and the implement fields in Implement.

Signal Unit Source tractor.csv Sensor Episode Vehicle Bridge CAN
Wheel spin rate rad/s Omega fl_omega_radps and the three other wheels - - -
Wheel slip ratio - SlipRatio fl_slip truth.jsonl: wheels[i].slip slip 0xFF12
Wheel load N NormalN fl_load_n wheels[i].normal_n - -
Wheel sinkage m SinkageM fl_sinkage_m wheels[i].sinkage_m - -
Forward speed m/s SpeedMps speed_mps truth.jsonl: speed_mps vx WBSD, 0xFF11
Steering angle rad SteeringRad steer_fl_rad, steer_fr_rad truth.jsonl: steer_rad steer_rad Guidance Machine Status, 0xFF13
Steering request rad SteeringRadians steer_command_rad actions.jsonl: steer_request_rad - can.jsonl, PGN 65280
Engine speed rev/min Rpm rpm truth.jsonl: rpm rpm EEC1
Gear and direction - Gear, Direction gear, direction actions.jsonl: gear, gear_direction - can.jsonl, PGN 65280
Throttle and brake 0 to 1 Throttle, Brake throttle, brake actions.jsonl - can.jsonl, PGN 65280
Differential lock - DiffLock diff_lock - difflock 0xFF16
Hitch lever, lift angle, mode -, deg, - HitchLever, LiftDeg, HitchMode hitch_lever, hitch_lift_deg, hitch_mode - hitch_lever, hitch_lift_deg, hitch_mode, raised -
PTO state, speed, power -, rev/min, kW bPtoOn, PtoRpm, PtoKW pto_on, pto_rpm, implement_pto_kw - pto_on, pto_rpm, pto_kw 0xFF18, power only
Tool depth m DepthM implement_depth_m - implement_depth_m 0xFF18
Implement draft N DraftN implement_draft_n - draft_n 0xFF17
Fuel rate, fuel used, fuel in the tank L/h, L, L FuelRateLph, FuelUsedL, FuelTankL fuel_lph, fuel_used_l, fuel_tank_l - - -

The wheel sequence is front left, front right, rear left, rear right. The columns of tractor.csv use the prefixes fl_, fr_, rl_ and rr_. The models of the signals are on the pages Maxxum 150 Dynamics, Tyre and Soil, Implement Mechanics and Energy and Fuel. The ROS 2 bridge publishes the engine speed, the steering angle and the implement state of the Maxxum from the vehicle bridge. The topics are engine_rpm, steering_angle and implement/state. The Topics page lists them.

Note

The hitch, the PTO and the fuel have no CAN command and no standard CAN frame in the simulator. Use the key implement of the vehicle bridge, or the ROS 2 topics hitch/lever_cmd, hitch/raise_cmd and pto/cmd.

Identifiers and Fields#

A frame has a 29-bit identifier. \(p\) is the priority, PGN is the parameter group number and SA is the source address.

\[ \text{id} = p \cdot 2^{26} + \text{PGN} \cdot 2^{8} + \text{SA} \]

The middle byte of the PGN is the PDU format byte (PF). With PF less than 0xF0, the low byte of the PGN field contains the destination address. The PGN is then the value with this byte set to zero. With PF of 0xF0 or more, the frame is a broadcast and the low byte is a part of the PGN. The functions are J1939Id in the game and make_id, parse_id in the codec.

Address Node
0x00 Engine
0x0B Brakes
0x2A Controller: the agent that sends commands
0x80 Tractor ECU and guidance controller
0xFF All nodes

This page numbers the bytes 0 to 7 and the bits 0 to 7 from the least significant bit. SAE and ISO documents number them 1 to 8. A field of more than one byte is little-endian. A byte that is not available is 0xFF. A field converts between its raw value and its physical value as follows.

\[ x = r\, s + o, \qquad r = \operatorname{clamp}\!\left( \operatorname{round}\!\left( \frac{x - o}{s} \right),\ r_{min},\ r_{max} \right) \]

Frames from the Tractor#

FAcresRlBridge::SendCan sends 13 frames for each observation, in the sequence of the table. An observation occurs each 0.1 s of simulation time.

Frame PGN Identifier Priority Source Content
EEC1 61444 (0xF004) 0x0CF00400 3 0x00 Engine speed
WBSD 65096 (0xFE48) 0x0CFE4880 3 0x80 Speed, distance, direction
Guidance Machine Status 44288 (0xAD00) 0x0CADFF80 3 0x80 Estimated curvature
Proprietary B, pose 65296 (0xFF10) 0x18FF1080 6 0x80 Position
Proprietary B, motion 65297 (0xFF11) 0x18FF1180 6 0x80 Yaw, velocity, yaw rate
Proprietary B, slip 65298 (0xFF12) 0x18FF1280 6 0x80 Wheel slip
Proprietary B, soil 65299 (0xFF13) 0x18FF1380 6 0x80 Soil water, steering angle
Proprietary B, range A 65300 (0xFF14) 0x18FF1480 6 0x80 Obstacle range, sectors 0 to 7
Proprietary B, range B 65301 (0xFF15) 0x18FF1580 6 0x80 Obstacle range, sectors 8 to 15
Proprietary B, state 65302 (0xFF16) 0x18FF1680 6 0x80 Slope, differential lock, flags, counters
Proprietary B, farm 65303 (0xFF17) 0x18FF1780 6 0x80 Harvest, crop damage, draft
Proprietary B, implement 65304 (0xFF18) 0x18FF1880 6 0x80 Implement, tool depth, PTO power
Proprietary B, complete 65311 (0xFF1F) 0x18FF1F80 6 0x80 Time, counter. The last frame of an observation.
Lockstep barrier 65310 (0xFF1E) 0x1CFF1E80 7 0x80 The step counter. In lockstep only.

The code names one suspect parameter number only: SPN 190, the engine speed. The other fields follow the layout of their parameter group. All field tables below have the same columns.

EEC1, PGN 61444#

Bytes Field Type Scale Offset Unit Description
3 to 4 Engine speed, SPN 190 uint16 0.125 0 rev/min \(r = \min(8 n_e, 64255)\). The maximum is 8031.875 rev/min.
0 to 2, 5 to 7 Not available 0xFF

WBSD, PGN 65096#

Bytes Field Type Scale Offset Unit Description
0 to 1 Wheel-based machine speed uint16 0.001 0 m/s The absolute value of \(v\). The maximum raw value is 64255.
2 to 5 Wheel-based machine distance uint32 0.001 0 m The distance since the start, modulo 4 211 081.215 m.
6 Not available 0xFF
7, bits 0 to 1 Machine direction 2 bits 1 forward, 0 reverse. The other bits are 1.

The game sets the speed of this frame to the forward speed of the chassis. It does not compute the speed from the wheel rotation. The frame thus shows no wheel slip. The distance is the sum of \(\lvert v \rvert\, \Delta t\) at each observation.

Guidance Machine Status, PGN 44288#

Bytes Field Type Scale Offset Unit Description
0 to 1 Estimated curvature uint16 0.25 -8032 1/km \(\kappa = 1000 \tan\delta / L\) from the actual steering angle.
2 Status bits 0xF4. Bits 0 to 1 are 00: the lockout is not active. Bits 2 to 3 are 01: the steering is ready.
3 to 7 Not available 0xFF

Proprietary B Frames#

PGN Bytes Field Type Scale Offset Unit Description
65296 0 to 3 Position X int32 0.01 0 m Unreal world, X to the east.
65296 4 to 7 Position Y int32 0.01 0 m Unreal world, Y to the south.
65297 0 to 1 Yaw uint16 0.01 0 deg Unreal yaw in the range 0 to 360. 0 is east, 90 is south.
65297 2 to 3 Velocity, forward int16 0.001 0 m/s Body axis x.
65297 4 to 5 Velocity, right int16 0.001 0 m/s Body axis y, positive to the right.
65297 6 to 7 Yaw rate int16 0.0001 0 rad/s Positive for a turn to the right.
65298 0 to 7 Wheel slip, 4 values int16 0.001 0 - Front left, front right, rear left, rear right.
65299 0 to 5 Soil water content, 3 values uint16 0.0001 0 m³/m³ Below the vehicle, 5 m ahead and 10 m ahead.
65299 6 to 7 Steering angle int16 0.0001 0 rad Positive to the left.
65300, 65301 0 to 7 Obstacle range, 8 sectors each uint8 0.12 0 m The maximum raw value is 250, which is 30 m.
65302 0 to 1 Cross slope int16 0.0001 0 rad Positive when the right side is lower.
65302 2 Differential lock uint8 1 0 - 0 off, 1 rear, 2 front and rear.
65302 3 Flags bits Bit 0 worker struck, bit 1 reset pending, bit 2 safety brake, bit 3 agent in control.
65302 4 to 5 Episode counter uint16 1 0 - The resets that the agent requested.
65302 6 Workers uint8 1 0 - The workers that the vehicle bridge placed.
65302 7 Resets applied uint8 1 0 - The low byte of the reset counter.
65303 0 to 1 Bunker mass uint16 1 0 kg
65303 2 to 3 Harvested mass uint16 1 0 kg
65303 4 to 5 Crushed crop area uint16 0.1 0 m²
65303 6 to 7 Implement draft uint16 10 0 N
65304 0 Implement kind uint8 1 0 - 0 to 7, see below. 0xFF with no implement.
65304 1 to 2 Mean tool depth uint16 0.001 0 m The maximum raw value is 64255.
65304 3 to 4 PTO shaft power uint16 0.1 0 kW The maximum raw value is 64255.
65311 0 to 3 Physics time uint32 0.001 0 s
65311 4 to 5 Time since the reset uint16 0.01 0 s The maximum is 655.35 s.
65311 6 Sequence counter uint8 1 0 - Increases by 1 for each observation.
65310 0 to 7 World step uint64 1 0 step The step that a step request reached.

The implement kinds are 0 cultivator, 1 chisel_plow, 2 disc_harrow, 3 seed_drill, 4 mounted_sprayer, 5 loader_bale_fork, 6 backhoe_attachment and 7 square_baler.

The obstacle range is not the LiDAR sensor. It is the nearest object in each of 16 sectors of 11.25° in front of the vehicle. Sector \(k\) covers the bearings from \(-90° + 11.25° k\) to \(-90° + 11.25° (k + 1)\) from the heading. The maximum range is 30 m. A controller collects the frames and uses them when frame 65311 arrives. The class ObservationAssembler of the codec does this.

Command Frames#

FAcresRlBridge::HandleCanFrame decodes four parameter groups. The destination address must be the address of the table or 0xFF.

Frame PGN Identifier Priority Source Destination Effect
Guidance System Command 44032 (0xAC00) 0x0CAC802A 3 0x2A 0x80 Steering
TSC1 0 (0x0000) 0x0C00002A 3 0x2A 0x00 Throttle
XBR 1024 (0x0400) 0x0C040B2A 3 0x2A 0x0B Brake
Proprietary A 61184 (0xEF00) 0x18EF802A 6 0x2A 0x80 Differential lock, reset, workers, HUD

The game does not examine the priority. It ignores a frame with less than 8 data bytes and a frame from a different source address.

Guidance System Command, PGN 44032#

Bytes Field Type Scale Offset Unit Description
0 to 1 Curvature command uint16 0.25 -8032 1/km Positive to the left.
2, bits 0 to 1 Steering command status 2 bits 1: the controller intends to steer. Other values put the wheels in the centre.
\[ \delta = \begin{cases} \operatorname{clamp}\!\left( \arctan\!\left( \dfrac{\kappa L}{1000} \right),\ -\delta_{max},\ \delta_{max} \right) & \text{status} = 1 \\ 0 & \text{if not} \end{cases} \]

One bit of the curvature is 0.25 km⁻¹. Near the centre this is a steering angle of 0.00066 rad for the Maxxum.

TSC1, PGN 0#

Bytes Field Type Scale Offset Unit Description
0, bits 0 to 1 Override control mode 2 bits 0 no override, 1 speed control, 2 torque control.
1 to 2 Requested speed uint16 0.125 0 rev/min Used in the mode 1.
3 Requested torque uint8 1 -125 % Used in the mode 2.
\[ u_t = \begin{cases} \operatorname{clamp}\!\left( \dfrac{r_3 - 125}{100},\ 0,\ 1 \right) & \text{mode 2} \\ \operatorname{clamp}\!\left( \dfrac{0.125\, r_{12} - n_e}{400},\ 0,\ 1 \right) & \text{mode 1} \\ 0 & \text{mode 0 or 3} \end{cases} \]

The torque mode is thus the throttle pedal. The speed mode is a proportional governor with a band of 400 rev/min.

XBR, PGN 1024#

Bytes Field Type Scale Offset Unit Description
0 to 1 External acceleration demand uint16 0.00048828125 -15.687 m/s² The scale is 1/2048.
2, bits 6 to 7 XBR control mode 2 bits 0: the override is off.
\[ u_b = \begin{cases} \operatorname{clamp}\!\left( \dfrac{-a_x}{4\ \text{m/s}^2},\ 0,\ 1 \right) & \text{mode} \ne 0 \text{ and } a_x < 0 \\ 0 & \text{if not} \end{cases} \]

A demand of -4 m/s² is the full brake.

Proprietary a, PGN 61184#

Byte 0 selects the message. The unused bytes are 0xFF.

Byte 0 Bytes Field Type Scale Offset Unit Description
0x01 1 Differential lock uint8 0 off, 1 rear, 2 front and rear. Other values keep the state.
0x01 2 Harvester uint8 0 off, 1 on. Other values keep the state.
0x01 3 Reset uint8 1: reset the vehicle to the staged pose.
0x01 4, 5 Place workers uint8 Byte 4 is 1: place the number of staged workers in byte 5.
0x01 6 Restore field uint8 1 with a reset: remove the marks of the field below the pose.
0x10 1 to 4 Reset position X int32 0.01 0 m Unreal world, X to the east.
0x11 1 to 4 Reset position Y int32 0.01 0 m Unreal world, Y to the south.
0x12 1 to 2 Reset yaw uint16 0.01 0 deg Unreal yaw, 0 to 360.
0x12 3 to 4 Reset speed uint16 0.001 0 m/s The start speed. The game limits it to 8 m/s.
0x21 1 Worker index uint8 0 to 7.
0x21 2 to 4 Worker X int24 0.01 0 m
0x21 5 to 7 Worker Y int24 0.01 0 m
0x30 1 to 4 Reward float32 The value that the HUD shows.
0x30 5 Success rate uint8 1 0 % 0 to 100. 0xFF shows no rate.

A reset with no staged position goes to the spawn pose of the session. The messages 0x10 and 0x11 stage the position. A reset starts a new episode. The marks on the fields stay unless byte 6 is 1. A front and rear lock needs a driven front axle. Workers need the option -NpcWorkers.

Timing and Safety#

Rule Value Source
Observation period 0.1 s of simulation time. The test uses a tolerance of half a physics step. ObservationPeriodS
Controller present A command frame arrived in the last 3 s of wall-clock time. CanPresenceS
Safety brake No steering, throttle or brake frame for 1 s of wall-clock time. The game then sets the brake to 1. ActionTimeoutS
Gear of the agent F10, forward. AgentGear
Lockstep The wall-clock limits do not apply. A command applies at the next physics step. FAcresSimControl::IsLockstep

While a controller is present, its commands replace the keyboard for the throttle, the brake, the steering, the gear and the differential lock. The frames Guidance System Command, TSC1 and XBR are the commands that drive the vehicle. A Proprietary A frame keeps the controller present but does not reset the 1 s timer.

A receiver thread reads the transports each millisecond and records the arrival time of each frame. A command that drives the vehicle then applies at the physics step that agrees with its arrival time. The Time Stepping and Determinism page gives this rule. The game sends the observation one time for each rendered frame at most. The period is thus 0.1 s or a small amount more. A frame rate below 10 Hz decreases the observation rate.

CAN Transport#

The game has two transports for the same frames. Both transports can be open at the same time.

Transport Option Description
CAN on UDP -RlCanUdp=<port> One frame in each UDP datagram on the address 127.0.0.1.
SocketCAN -RlCan=<interface> A raw socket on a Linux CAN interface, for example vcan0.

The two options apply to agent 0. A different agent sets the key can_udp in the agents file. The transports are for Linux only. On other platforms the game writes ACRES_RL_CAN_UNSUPPORTED to the log.

A datagram is the 16 bytes of the Linux structure can_frame.

Offset Type Field
0 uint32, little-endian The identifier with the flag 0x80000000 for a 29-bit identifier.
4 uint8 The data length, 8.
5 3 bytes Padding.
8 8 bytes The data.

The game ignores frames with an 11-bit identifier, remote frames and error frames. The UDP socket of the game has no fixed peer. The game sends its frames to the address of the last datagram that it received. Thus a controller must send one datagram before it gets frames. The SocketCAN transport sends frames at all times.

The ROS 2 bridge connects to the UDP port with the parameter can_udp_port.

Topic Direction Message Description
can/rx Published can_msgs/Frame The frames from the tractor. The stamp is the simulation time and the frame name is can.
can/tx Subscribed can_msgs/Frame The frames to the tractor.

The two topics have a queue depth of 500, as the nodes of ros2_socketcan have. The bridge sends a keep-alive frame at the start and then each second: a Proprietary A frame from the source address 0xFE with 0xFF data. The game learns the peer from this frame and ignores its content.

In lockstep, the game sends the barrier frame 65310 after all frames of a step request. The ROS 2 bridge counts this frame for the step reply and does not publish it on can/rx. The Lockstep Stepping tutorial shows the barrier.

Codec#

The codec exists in Python and in C++. The two give the same bytes, and a test compares them.

Language File Use
Python ROS/acres_sim/acres_sim/j1939.py import acres_sim.j1939
C++ ROS/acres_sim/include/acres_sim/j1939.hpp A header without ROS 2, namespace acres_sim::j1939
from acres_sim import j1939

frames = [
    j1939.encode_gsc(50.0),            # curvature 50 km^-1, steer
    j1939.encode_tsc1_torque(0.45),    # throttle 45 %
    j1939.encode_xbr(0.5),             # brake 50 %
]
for can_id, data in frames:
    print(j1939.describe(can_id, data, 2.6416))
#include <cstdio>

#include "acres_sim/j1939.hpp"

int main()
{
    using namespace acres_sim::j1939;
    for (const Frame& f : {encode_gsc(50.0), encode_tsc1_torque(0.45), encode_xbr(0.5)})
        std::puts(describe(f.id, {f.data.begin(), f.data.end()}).c_str());
}

Expected Result

0CAC802A  [8] 48 7E FD FF FF FF FF FF  GSC 2A->80 curvature=+50.00/km (+7.5 deg) steer
0C00002A  [8] FE FF FF AA FF FF FF FF  TSC1 2A->00 mode=torque torque=45%
0C040B2A  [8] 7F 6D BF FF FF FF FF FF  XBR 2A->0B accel=-2.00m/s2 mode=2

These are the 13 frames of the first observation of a session, as describe shows them. The Maxxum is at rest at the default spawn.

0CF00400  [8] FF FF FF EF 18 FF FF FF  EEC1 00->FF rpm=798
0CFE4880  [8] 00 00 00 00 00 00 FF FD  WBSD 80->FF speed=0.000m/s dir=fwd
0CADFF80  [8] 80 7D F4 FF FF FF FF FF  GMS 80->FF est. curvature=+0.00/km
18FF1080  [8] E1 F0 FF FF E3 36 00 00  Pose 80->FF x=-38.71 y=140.51
18FF1180  [8] 78 69 00 00 00 00 00 00  Motion 80->FF yaw=270.00 vx=0.000 vy=0.000 r=+0.0000
18FF1280  [8] 00 00 00 00 00 00 00 00  Slip 80->FF slip=+0.000,+0.000,+0.000,+0.000
18FF1380  [8] D0 07 D0 07 D0 07 00 00  Soil 80->FF theta=0.200/0.200/0.200 steer=+0.0000rad
18FF1480  [8] FA FA FA FA FA FA FA FA  LidarA 80->FF m=30.0,30.0,30.0,30.0,30.0,30.0,30.0,30.0
18FF1580  [8] FA BC D5 F9 FA FA FA FA  LidarB 80->FF m=30.0,22.6,25.6,29.9,30.0,30.0,30.0,30.0
18FF1680  [8] 00 00 00 08 00 00 00 00  State 80->FF slope=+0.0000 difflock=0 flags=1000 episode=0 workers=0
18FF1780  [8] 00 00 00 00 00 00 00 00  Farm 80->FF
18FF1880  [8] FF 00 00 00 00 FF FF FF  Implement 80->FF
18FF1F80  [8] 2A 00 00 00 04 00 00 FF  ObsDone 80->FF t=0.04s ep_t=0.04s seq=0

The codec rounds a half to the even value, as Python does. The game rounds a half up. The two differ only for a value at the middle of a bit.

Example: A Controller on the UDP Transport#

This example drives the Maxxum on a circle with CAN frames only. It does not use ROS 2.

  1. Start the game with the CAN transport on UDP port 5556.

    Packaged/Linux/Acres.sh -VehicleDemo -RlCanUdp=5556
    

    Expected Result

    The log contains the line ACRES_RL_CAN_UDP_OPEN port=5556.

  2. Write this program into the file can_client.py.

    import socket
    import struct
    import time
    
    from acres_sim import j1939
    
    game = ("127.0.0.1", 5556)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.settimeout(0.02)
    commands = [j1939.encode_gsc(50.0), j1939.encode_tsc1_torque(0.45), j1939.encode_xbr(0.0)]
    assembler = j1939.ObservationAssembler()
    next_send = 0.0
    while True:
        if time.monotonic() >= next_send:
            for can_id, data in commands:
                sock.sendto(struct.pack("<IB3x8s", can_id | 0x80000000, 8, data), game)
            next_send = time.monotonic() + 0.1
        try:
            raw = sock.recv(16)
        except (socket.timeout, ConnectionRefusedError):
            continue
        can_id, _, data = struct.unpack("<IB3x8s", raw)
        observation = assembler.feed(can_id & 0x1FFFFFFF, data)
        if observation and observation["sequence"] % 20 == 0:
            print(f'{observation["t"]:6.1f} s  {observation["speed"]:5.2f} m/s  {observation["curvature_km"]:5.1f} 1/km',
                  flush=True)
    
  3. Start the program in a second terminal.

    PYTHONPATH=ROS/acres_sim python3 can_client.py
    

    Expected Result

    The Maxxum moves and turns to the left. The program prints one line for each 20 observations.

       0.0 s   0.00 m/s    0.0 1/km
       2.1 s   2.37 m/s   50.0 1/km
       4.1 s   2.34 m/s   50.0 1/km
       6.1 s   2.34 m/s   50.0 1/km
       8.1 s   2.34 m/s   50.0 1/km
      10.2 s   2.34 m/s   50.0 1/km
    
  4. Stop the program with Ctrl+C.

    Expected Result

    The game sets the brake after 1 s without commands. After 3 s the keyboard has the control again.

The program sends the three command frames each 0.1 s. The curvature command is 50 km⁻¹, which is a circle of 20 m radius. The estimated curvature of the status frame goes to the commanded value when the steering reaches its angle. At the default spawn, the circle meets a building after approximately 45 s. The speed then goes to zero.

Recorded CAN File#

The sensor recorder writes can.jsonl in the episode folder. It is independent of the live bus. It writes two rows at the rate can.hz, with no latency. The Polaris overlay sets can.enabled to false.

Row Identifier PGN Bytes Field Type Scale Offset Unit
1 0x0CF00480 61444 3 to 4 Engine speed, SPN 190 uint16 0.125 0 rev/min
2 0x18FF0080 65280 0 Throttle uint8 0.4 0 %
2 0x18FF0080 65280 1 Brake uint8 0.4 0 %
2 0x18FF0080 65280 2 to 3 Steering request uint16 0.01 -327.68 deg
2 0x18FF0080 65280 4 Direction uint8 1 forward, 2 reverse

The other bytes are 255. PGN 65280 is a format of ACRES. It is not a standard actuation message of a tractor. Row 1 has the source address 0x80 in the file. The live bus sends EEC1 from the address 0x00.

Key Type Description
time_s number The episode time of the sample.
extended boolean true: a 29-bit identifier.
id_hex string The identifier as 8 hexadecimal digits.
pgn integer The parameter group number.
data array of 8 The data bytes as integers.
direction string feedback for row 1 and command for row 2.
source string Row 2 only. The control source, for example manual, replay or scripted_test.
mapping string A description of the frame.

These are the first two rows of a recording. The engine speed is \(6395 \times 0.125 = 799.4\) rev/min and the brake is 100 %.

{"time_s": 0.0, "extended": true, "id_hex": "0CF00480", "pgn": 61444, "data": [255, 255, 255, 251, 24, 255, 255, 255], "direction": "feedback", "mapping": "EEC1 SPN190 only; remaining bytes unavailable"}
{"time_s": 0.0, "extended": true, "id_hex": "18FF0080", "pgn": 65280, "data": [0, 250, 0, 128, 1, 255, 255, 255], "direction": "command", "source": "scripted_test", "mapping": "ACRES proprietary v1; NOT a standard tractor actuation PGN"}

Quantisation and Noise#

The CAN signals have no noise model and no latency model. The column "Scale" of each field is its quantisation step.

Effect Rule
Rounding To the nearest raw value. A half goes up.
Limits The raw value stays in the range of its type. The engine speed and the speed of WBSD stop at 64255.
Angle The yaw of frame 65297 is modulo 360°.
Counters The distance, the physics time, the episode counter and the sequence counter go back to zero at the end of their range.
Time since the reset The value stops at 655.35 s.
Sample time A frame contains the state at the game frame that sends it. The frame has no time stamp of its own.

The file can.jsonl uses the same rounding. Its rows show the state at the physics step of time_s.

Parameters#

Name Type Unit Default Description
-RlCanUdp= integer off The UDP port of the CAN transport for agent 0.
-RlCan= string off The SocketCAN interface for agent 0.
can_udp integer off The key of an agent in the agents file: the UDP port of that agent.
can_udp_port integer 0 The parameter of the ROS 2 bridge: the UDP port of the game. 0 sets the topics off.
can.enabled boolean true The key of sensors.json: the recorder writes can.jsonl.
can.hz number Hz 20 The key of sensors.json: the rate of can.jsonl. Range 1 to 120.
-SensorNoCan flag Sets can.jsonl off.

The constants of the table "Timing and Safety" are in the code. No configuration file changes them.

Code Map#

Item File Function
Encoder of the tractor frames Acres/Source/Acres/AcresRlBridge.cpp FAcresRlBridge::SendCan, J1939Id
Decoder of the command frames AcresRlBridge.cpp FAcresRlBridge::HandleCanFrame
Observation, obstacle range, distance AcresRlBridge.cpp Observe, TraceLidar
Transports AcresRlBridge.cpp ReadCan, WriteCan, the constructor
Controller present, safety brake AcresRlBridge.cpp CanInControl, ActionStale, CurrentControls
Lockstep barrier AcresRlBridge.cpp SendBarrier
Options Acres/Source/Acres/AcresVehicle.cpp AAcresVehiclePawn::BeginPlay
Recorded CAN rows Acres/Source/Acres/AcresSensors.cpp FAcresSensorRecorder::PhysicsSample
Session log Acres/Source/Acres/AcresSessionLog.cpp FAcresSessionLog::Open, Push
Codec, Python ROS/acres_sim/acres_sim/j1939.py encode_*, decode_*, ObservationAssembler, describe
Codec, C++ ROS/acres_sim/include/acres_sim/j1939.hpp The same names
UDP client of the ROS 2 bridge ROS/acres_sim/src/stream.cpp UdpCanClient, write_can_frame, read_can_frame
CAN topics ROS/acres_sim/src/vehicle_bridge.cpp, conversions.cpp VehicleBridge::on_can, to_can_msg, from_can_msg
Tests ROS/acres_sim/test/test_j1939.cpp, test_j1939_golden.py, j1939_golden.cpp Identifiers, round trips, C++ against Python
Check with the game Tools/SimControl/can_over_ros_check.py A guidance command on can/tx moves the tractor

Limitations#

  • The bus has the frames of this page only. It has no address claim, no requests and no diagnostic messages.
  • The wheel-based speed is the speed of the chassis. The simulator has no model of a wheel speed sensor with slip and pulses.
  • The hitch, the PTO and the fuel are not on the bus as standard ISO 11783 frames.
  • The speed mode of TSC1 is a proportional governor, not the engine controller of the vehicle model.
  • The brake command is a linear map of the acceleration demand. It is not a brake controller with feedback.
  • The observation rate depends on the frame rate of the game when the frame rate is below 10 Hz.
  • The frame 65304 is in the game and in the decoder of the codec. The function encode_observation of the codec does not make it.
  • ACRES Core has no CAN bus.

References#

  • SAE International. SAE J1939-21: Data Link Layer and SAE J1939-71: Vehicle Application Layer (EEC1, TSC1, XBR).
  • International Organization for Standardization. ISO 11783-7: Tractors and machinery for agriculture and forestry, Serial control and communications data network, Part 7: Implement messages application layer (WBSD, guidance messages).
  • Linux kernel documentation. SocketCAN: Controller Area Network, the structure can_frame.